[英]How can I get non-blocking socket connect()'s?
我在这里有一个非常简单的问题。 我需要同时与许多主机通信,但我真的不需要任何同步,因为每个请求都非常自给自足。
因此,我选择使用异步套接字,而不是垃圾邮件线程。 现在我确实有一个小问题:
异步的东西就像一个魅力,但是当我连接到 100 台主机时,我得到 100 次超时(超时 = 10 秒)然后我等待 1000 秒,只是为了找出我所有的连接都失败了。
有什么办法也可以获得非阻塞套接字连接吗? 我的套接字已设置为 nonBlocking,但对 connect() 的调用仍处于阻塞状态。
减少超时不是一个可接受的解决方案。
我在 Python 中执行此操作,但我想在这种情况下编程语言并不重要。
我真的需要使用线程吗?
使用select
模块。 这允许您等待多个非阻塞套接字上的I / O完成。 这里有一些关于选择的更多信息 。 从链接到页面:
在C中,编码
select
相当复杂。 在Python中,它是一块蛋糕,但它与C版本足够接近,如果您理解Python中的select,那么在C中你会遇到一些麻烦。
ready_to_read, ready_to_write, in_error = select.select(
potential_readers,
potential_writers,
potential_errs,
timeout)
您传递了
select
三个列表:第一个包含您可能想要尝试阅读的所有套接字; 您可能想要尝试写入的第二个所有套接字,以及要检查错误的最后一个(通常为空)。 您应该注意,套接字可以进入多个列表。select
调用是阻塞的,但你可以给它一个超时。 这通常是一件明智的事情 - 给它一个很长的超时(比如一分钟),除非你有充分的理由不这样做。作为回报,您将获得三个列表。 它们具有实际可读,可写和错误的套接字。 这些列表中的每一个都是您传入的相应列表的子集(可能为空)。如果您将套接字放在多个输入列表中,它将只(最多)在一个输出列表中。
如果一个套接字在输出可读列表中,那么你可以像我们一样接近这个业务,以便该套接字上的
recv
将返回一些东西。 可写清单的想法相同。 你将能够send
一些东西。 也许不是你想要的全部,但有些东西总比没有好。 (实际上,任何合理健康的套接字都将返回为可写 - 它只是意味着出站网络缓冲区空间可用。)如果您有“服务器”套接字,请将其放在potential_readers列表中。 如果它出现在可读列表中,那么您的接受(几乎肯定)会起作用。 如果您已创建新套接字以连接到其他人,请将其放在potential_writers列表中。 如果它出现在可写列表中,那么它有很大的可能性已连接。
不幸的是,没有示例代码显示错误,所以有点难以看出这个块来自何处。
他做了类似的事情:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(0)
s.connect(("www.nonexistingname.org", 80))
套接字模块在内部使用getaddrinfo,这是一个阻塞操作,尤其是当主机名不存在时。 符合标准的dns客户端将等待一段时间,看看该名称是否确实存在,或者是否只涉及一些慢速DNS服务器。
解决方案是仅连接到ip-addresses或使用允许非阻塞请求的dns客户端,如pydns 。
您还需要并行化连接,因为设置超时时套接字会阻塞。 或者,您无法设置超时,并使用选择模块。
您可以使用asyncore模块中的调度程序类执行此操作。 看一下基本的http客户端示例 。 该类的多个实例不会在连接上相互阻塞。 您可以使用线程轻松完成此操作,我认为使跟踪套接字超时更容易,但由于您已经在使用异步方法,因此您也可以保持在同一轨道上。
例如,以下内容适用于我的所有Linux系统
import asyncore, socket
class client(asyncore.dispatcher):
def __init__(self, host):
self.host = host
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, 22))
def handle_connect(self):
print 'Connected to', self.host
def handle_close(self):
self.close()
def handle_write(self):
self.send('')
def handle_read(self):
print ' ', self.recv(1024)
clients = []
for i in range(50, 100):
clients.append(client('cluster%d' % i))
asyncore.loop()
在cluster50 - cluster100中,有许多机器没有响应或不存在。 这会立即开始打印:
Connected to cluster50
SSH-2.0-OpenSSH_4.3
Connected to cluster51
SSH-2.0-OpenSSH_4.3
Connected to cluster52
SSH-2.0-OpenSSH_4.3
Connected to cluster60
SSH-2.0-OpenSSH_4.3
Connected to cluster61
SSH-2.0-OpenSSH_4.3
...
然而,这并未考虑必须阻止的getaddrinfo。 如果您在解决dns查询时遇到问题,那么一切都必须等待。 您可能需要单独收集dns查询,并在异步循环中使用ip地址
如果你想要一个比asyncore更大的工具包,请看看Twisted Matrix 。 进入它有点沉重,但它是python可以获得的最好的网络编程工具包。
使用扭曲 。
它是一个用Python编写的异步网络引擎,支持多种协议,您可以添加自己的协议。 它可用于开发客户端和服务器。 它不会阻止连接。
你看过asyncore模块了吗? 可能就是你需要的。
将socket.connect
与非阻塞套接字一起使用时,最初可能会出现BlockingIOError
。 请参阅TCP 连接错误 115 正在进行的操作是什么原因? 原因的解释。
解决方案是捕获并忽略异常或使用socket.connect_ex
而不是socket.connect
因为该方法不会引发异常。 特别注意 Python 文档中描述的最后一句话:
socket.connect_ex(address)
与
connect(address)
类似,但返回一个错误指示符,而不是为 C 级 connect() 调用返回的错误引发异常(其他问题,例如“找不到主机”,仍可能引发异常)。 如果操作成功,则错误指示符为 0,否则为 errno 变量的值。 这对于支持异步连接等很有用。
来源: https : //docs.python.org/3/library/socket.html#socket.socket.connect_ex
如果您想继续使用socket.connect
,您可以捕获并忽略负责的EINPROGRESS
错误:
>>> import socket
>>>
>>> # bad
>>> s = socket.socket()
>>> s.setblocking(False)
>>> s.connect(("127.0.0.1", 8080))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
BlockingIOError: [Errno 115] Operation now in progress
>>>
>>> # good
>>> s = socket.socket()
>>> s.setblocking(False)
>>> try:
... s.connect(("127.0.0.1", 8080))
... except OSError as exc:
... if exc.errno != 115: # EINPROGRESS
... raise
...
>>>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.