Mik*_*ava 6 python sockets networking nonblocking
在Python中,我想socket.connect()在设置为非阻塞的套接字上使用。当我尝试这样做时,该方法总是抛出一个BlockingIOError. 当我忽略错误(如下所示)时,程序将按预期执行。当我在连接后将套接字设置为非阻塞时,没有错误。当我使用 select.select() 确保套接字可读或可写时,我仍然收到错误。
测试服务器.py
import socket
import select
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(0)
host = socket.gethostname()
port = 1234
sock.bind((host, port))
sock.listen(5)
while True:
select.select([sock], [], [])
con, addr = sock.accept()
message = con.recv(1024).decode('UTF-8')
print(message)
Run Code Online (Sandbox Code Playgroud)
测试客户端.py
import socket
import select
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(0)
host = socket.gethostname()
port = 1234
try:
sock.connect((host, port))
except BlockingIOError as e:
print("BlockingIOError")
msg = "--> From the client\n"
select.select([], [sock], [])
if sock.send(bytes(msg, 'UTF-8')) == len(msg):
print("sent ", repr(msg), " successfully.")
sock.close()
Run Code Online (Sandbox Code Playgroud)
1号航站楼
$ python testserver.py
--> From the client
Run Code Online (Sandbox Code Playgroud)
2号航站楼
$ python testclient.py
BlockingIOError
sent '--> From the client\n' successfully.
Run Code Online (Sandbox Code Playgroud)
除了第一个 connect() 上的 BlockingIOError 之外,此代码可以正常工作。该错误的文档如下所示:Raised when an operation would block on an object (e.g. socket) set for non-blocking operation.
如何正确使用 connect() 将套接字设置为非阻塞?我可以使 connect() 非阻塞吗?或者忽略该错误是否合适?
当socket.connect与非阻塞套接字一起使用时,BlockingIOError首先预计会得到一个。请参阅TCP 连接错误 115 操作正在进行 原因是什么?原因的解释。基本上,套接字尚未准备好并引发BlockingIOError: [Errno 115] Operation now in progress,也称为EINPROGRESS。
解决方案是捕获并忽略异常,或者使用socket.connect_ex代替,socket.connect因为该方法不会引发异常。特别注意 Python 文档中描述的最后一句话:
\n\n\n
socket.connect_ex(address)与 类似
\nconnect(address),但返回一个错误指示符,而不是针对 C 级 connect() 调用返回的错误引发异常(其他问题,例如 \xe2\x80\x9chost 未找到,\xe2\x80\x9d 仍然会引发异常) 。如果操作成功,则错误指示符为 0,否则为 errno 变量的值。例如,这对于支持异步连接很有用。
来源:https ://docs.python.org/3/library/socket.html#socket.socket.connect_ex
\n如果你想继续使用socket.connect,你可以捕获并忽略负责的EINPROGRESS错误:
>>> import socket\n>>> \n>>> # bad\n>>> s = socket.socket()\n>>> s.setblocking(False)\n>>> s.connect(("127.0.0.1", 8080))\nTraceback (most recent call last):\n File "<stdin>", line 1, in <module>\nBlockingIOError: [Errno 115] Operation now in progress\n>>> \n>>> # good\n>>> s = socket.socket()\n>>> s.setblocking(False)\n>>> try:\n... s.connect(("127.0.0.1", 8080))\n... except OSError as exc:\n... if exc.errno != 115: # EINPROGRESS\n... raise\n... \n>>> \nRun Code Online (Sandbox Code Playgroud)\n
这里的技巧是,当第一次选择完成时,您需要sock.connect再次调用。在您收到来自 的成功返回状态之前,套接字不会连接connect。
只需在第一次调用完成后添加这两行即可select:
print("first select completed")
sock.connect((host, port))
Run Code Online (Sandbox Code Playgroud)
编辑:
后续。我错误地指出需要额外致电sock.connect。connect然而,如果您希望在其自己的代码路径中处理连接失败,那么这是发现原始非阻塞调用是否成功的好方法。
这里解释了用 C 代码实现此目的的传统方法:Async connect and disconnect with epoll (Linux)
这涉及到调用getsockopt。你也可以在 python 中执行此操作,但返回的结果sock.getsockopt是一个bytes对象。如果它代表失败,则需要将其转换为整数值errno并将其映射到字符串(或异常或将问题传达给外界所需的任何内容)。再次调用已将值sock.connect映射errno到适当的异常。
解决方案 2:您也可以简单地将调用推迟sock.setblocking(0)到连接完成之后。
| 归档时间: |
|
| 查看次数: |
10085 次 |
| 最近记录: |