繁体   English   中英

如何获得非阻塞套接字连接()?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM