我需要在Python的socket recv方法上设置超时。如何实现?
通常的方法是使用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()
为套接字上的所有操作设置超时,但我看到您在另一个答案中明确拒绝了该解决方案。select
很好,但你所说的“你不能”这部分是误导性的,因为有 socket.settimeout()
方法。 - noskloselect
时需要注意——如果你在 Windows 机器上运行,select
依赖于 WinSock 库,该库有一个习惯,即只要有一些数据到达,就会立即返回,但不一定是所有数据。因此,您需要加入一个循环来不断调用 select.select()
直到接收到所有数据。如何知道已经获取了所有数据(不幸的是)取决于您自己去找出来——这可能意味着查找终止符字符串、特定数量的字节,或者只是等待定义的超时时间。 - JDMready[0]
是否只会为false? - matansterselectors
是select
的高级替代品。 - Jarek Przygódzki有一个名为socket.settimeout()
的函数。
select
的解决方案会更受欢迎,因为这个解决方案只有一行代码(更易于维护,在实现时出错的风险也较小),而且在底层也使用了select
(实现与@DanielStuzbach答案相同)。 - Trevor Boyd Smith如上所述,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)
.settimeout()
方法,建议您首先调用 setdefaulttimeout()
方法。该方法可设置全局默认超时时间。 - mvarge您可以在接收响应之前设置超时时间,在接收到响应后将其设置为无:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5.0)
data = sock.recv(1024)
sock.settimeout(None)
如果您在实施服务器端,则寻找的超时时间是连接套接字的超时时间,而不是主要套接字的超时时间。换句话说,连接套接字对象有另一个超时时间,它是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)
我有点被顶部回答给搞糊涂了,所以我写了一个小的代码片段来帮助更好地理解。
选项#1-socket.settimeout()
如果sock.recv()
等待的时间超过定义的超时时间,它将引发异常。
import socket
sock = socket.create_connection(('neverssl.com', 80))
timeout_seconds = 2
sock.settimeout(timeout_seconds)
sock.send(b'GET / HTTP/1.1\r\nHost: neverssl.com\r\n\r\n')
data = sock.recv(4096)
data = sock.recv(4096) # <- will raise a socket.timeout exception here
选项 #2 - select.select()
等待数据发送直到超时时间被触发。我对Daniel的回答进行了修改,使其会抛出异常。
import select
import socket
def recv_timeout(sock, bytes_to_read, timeout_seconds):
sock.setblocking(0)
ready = select.select([sock], [], [], timeout_seconds)
if ready[0]:
return sock.recv(bytes_to_read)
raise socket.timeout()
sock = socket.create_connection(('neverssl.com', 80))
timeout_seconds = 2
sock.send(b'GET / HTTP/1.1\r\nHost: neverssl.com\r\n\r\n')
data = recv_timeout(sock, 4096, timeout_seconds)
data = recv_timeout(sock, 4096, timeout_seconds) # <- will raise a socket.timeout exception here
试试这个,它使用底层的 C 语言。
timeval = struct.pack('ll', 2, 100)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
SO_RCVTIMEO
和 SO_SNDTIMEO
设置不同的发送和接收超时值。 - jtpereyda2
和 100
?超时值是哪个?使用什么单位表示? - Alfetimeval = struct.pack('ll', sec, usec)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, timeval)
其中usec = 10000表示10毫秒。 - Tavy您可以使用 socket.settimeout()
方法,该方法接受一个整数参数表示超时时间(秒)。例如,socket.settimeout(1)
将超时时间设置为 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
如之前回复中提到的,您可以使用以下代码:.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()
我希望这可以帮助您!