如何在python的socket recv方法上设置超时?


Answers:


125

典型的方法是使用select()等待数据可用或超时。仅recv()在实际可用数据时调用。为了安全起见,我们还将套接字设置为非阻塞模式,以确保recv()永远不会无限阻塞。 select()也可以一次在多个插座上等待。

import select

mysocket.setblocking(0)

ready = select.select([mysocket], [], [], timeout_in_seconds)
if ready[0]:
    data = mysocket.recv(4096)

如果您有很多打开的文件描述符,则poll()是更有效的替代方法select()

另一个选择是使用设置套接字上所有操作的超时socket.settimeout(),但是我看到您在另一个答案中明确拒绝了该解决方案。


16
使用select是好的,但是您说“不能”的部分会误导您,因为存在socket.settimeout()
nosklo

1
现在好多了,但是我看不出答案被“明确拒绝”了。
nosklo

7
使用时要小心select-如果您在Windows计算机select上运行,则依赖WinSock库,该库习惯于在某些数据到达(但不一定全部)后立即返回。因此,您需要合并一个循环以继续调用,select.select()直到接收到所有数据为止。您知道如何获得所有数据(很不幸)取决于您-可能意味着寻找终止符字符串,一定数量的字节,或仅等待定义的超时。
JDM

4
为什么必须将套接字设置为非阻塞?我认为这对select调用无关紧要(在这种情况下它将阻塞直到可以读取描述符或超时为止),并且如果满足select则recv()不会阻塞。我使用recvfrom()进行了尝试,似乎没有setblocking(0)即可正常工作。
HankB 2015年

1
ready[0]只能是假的,如果有在服务器的响应没有身体?
matanster '18

60

16
它不会使recv超时(至少在我尝试时)。仅accept()超时。
Oren S

9
完全按照预期设置socket.settimeout()后,socket.recv()对我来说似乎超时。我要化妆吗?有人可以确认吗?
Aeonaut

3
@Aeonaut我认为这在大多数情况下都会使recv()超时,但是存在竞争条件。在socket.recv()中,Python(2.6)在内部使用超时调用select / poll,然后立即调用recv()。因此,如果使用阻塞套接字,并且在这两个调用之间发生另一个端点崩溃,则可能会无限期地挂在recv()上。如果您使用非阻塞套接字,则python不会在内部调用select.select,因此我认为Daniel Stutzbach的答案是正确的方法。
emil.p.stanchev 2012年

4
实际上,当select()返回时,我可能会误解了,所以请抓取前面的注释。Beej的指南说,以上是在recv上实现超时的有效方法:beej.us/guide/bgnet/output/html/singlepage/…因此,我相信这是一个权威来源。
emil.p.stanchev 2012年

2
我不确定为什么select当此解决方案是一个衬套(易于维护,实现错误的风险较小)并且在幕后使用select时首选使用的解决方案(实现与@DanielStuzbach答案相同)。
Trevor Boyd Smith,

33

如前所述都select.select()socket.settimeout()正常工作。

请注意,您可能需要打settimeout两次电话,例如

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(("",0))
sock.listen(1)
# accept can throw socket.timeout
sock.settimeout(5.0)
conn, addr = sock.accept()

# recv can throw socket.timeout
conn.settimeout(5.0)
conn.recv(1024)

3
我认为无论您如何戳戳和刺探该函数挂起的位置,他都会遇到同样的问题。我已经尝试2或4次超时,但仍然挂起。settimeout也挂起。
凯西·丹尼尔

1
当您.settimeout()多次调用时,您可以首先调用该setdefaulttimeout()方法。
mvarge

12

您可以在收到响应之前设置超时,在收到响应之后将其设置回无:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)

5

如果要实现服务器端,则要查找的超时是连接套接字的超时,而不是主套接字的超时。换句话说,连接套接字对象还有另一个超时,这是socket.accept()方法的输出。因此:

sock.listen(1)
connection, client_address = sock.accept()
connection.settimeout(5)    # This is the one that affects recv() method.
connection.gettimeout()     # This should result 5
sock.gettimeout()           # This outputs None when not set previously, if I remember correctly.

如果实现客户端,那将很简单。

sock.connect(server_address)
sock.settimeout(3)

2

如先前的答复所述,您可以使用类似以下内容的.settimeout() 示例:

import socket

s = socket.socket()

s.settimeout(1) # Sets the socket to timeout after 1 second of no activity

host, port = "somehost", 4444
s.connect((host, port))

s.send("Hello World!\r\n")

try:
    rec = s.recv(100) # try to receive 100 bytes
except socket.timeout: # fail after 1 second of no activity
    print("Didn't receive data! [Timeout]")
finally:
    s.close()

我希望这有帮助!!


2

您可以使用socket.settimeout()接受代表秒数的整数参数。例如,socket.settimeout(1)将超时设置为1秒


2

尝试使用基础C。

timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)

这很棒,因为它允许使用SO_RCVTIMEO和设置发送和接收超时的不同值SO_SNDTIMEO
jtpereyda

为什么2和为什么100?哪个超时值?在什么单位?
Alfe

timeval = struct.pack('ll', sec, usec) s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)usec = 10000表示10毫秒
Tavy

1
#! /usr/bin/python3.6

# -*- coding: utf-8 -*-
import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.settimeout(5)
PORT = 10801

s.bind(('', PORT))
print('Listening for broadcast at ', s.getsockname())
BUFFER_SIZE = 4096
while True:
    try:
        data, address = s.recvfrom(BUFFER_SIZE)
    except socket.timeout:
        print("Didn't receive data! [Timeout 5s]")
        continue

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.