python-3.x - Python socket.recv 与 MSG_DONTWAIT
问题描述
我几乎总是在阻塞模式下接收套接字,这工作正常。偶尔我不想等待 - 如果套接字上有数据我现在就想要它,否则我稍后再试。
我以为我可以使用 flags 参数来做到这一点socket.recv()
,但它似乎不起作用。我可以使用socket.setblocking()
andsocket.settimeout()
调用来达到我想要的效果,但这似乎很笨拙。
从python 套接字文档中, flags 参数与 Unix recv具有相同的含义:
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, the
call fails with the error EAGAIN or EWOULDBLOCK. This provides
similar behavior to setting the O_NONBLOCK flag (via the fcntl(2)
F_SETFL operation), but differs in that MSG_DONTWAIT is a per-call
option, whereas O_NONBLOCK is a setting on the open file description
(see open(2)), which will affect all threads in the calling process
and as well as other processes that hold file descriptors referring
to the same open file description.
我读到这意味着我可以通过socket.MSG_DONTWAIT
仅对该调用进行非阻塞操作。可能这是不正确的 - 我也可以阅读它,因为它总是会返回错误,因为原则上调用会阻塞。在这种情况下,这一切都无关紧要。
一些示例代码:
- 一个简单的阻塞调用。正如预期的那样,这大约需要 0.5 秒。
- MSG_DONTWAIT:我希望是一个非阻塞调用,很快就会返回。实际上这也花了大约0.5s
- 通过重新配置端口实现非阻塞。这实际上需要大约 50us,所以它显然没有像前两个调用那样阻塞。
import socket
import time
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.settimeout(0.5)
starttime = time.time()
try:
m = sock.recv(100)
except socket.timeout as e:
pass
endtime = time.time()
print(f'sock.recv(100) took {endtime-starttime}s') # 0.5s
starttime = time.time()
try:
m = sock.recv(100, socket.MSG_DONTWAIT)
except socket.timeout as e:
pass
endtime = time.time()
print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s') # 0.5s
starttime = time.time()
timeout = sock.gettimeout()
sock.setblocking(0)
try:
m = sock.recv(100)
except BlockingIOError as e:
pass
sock.settimeout(timeout)
endtime = time.time()
print(f'sock.recv(100) with non-blocking set took {endtime-starttime}s') # 4.96e-5s
问题:
- 我对 MSG_DONTWAIT 的使用是错误的吗?它应该以我尝试使用它的方式工作吗?
- 有没有更好的方法来切换对 recv() 的阻塞和非阻塞调用
解决方案
关于“1.我对使用的错误MSG_DONTWAIT
吗?它应该以我尝试使用它的方式工作吗?”:
不,您没有错,但是您的测试方式存在一个小问题。具体来说,您的
MSG_DONTWAIT
测试是针对超时为0.5s
. 这是因为您sock.settimeout(0.5)
在第一次测试之前就有了(也许您忽略了这会影响您的第二次测试)。
如果我在您的测试中更新异常类型MSG_DONTWAIT
(这是套接字阻塞的另一个迹象)并在“干净”会话中尝试,我会得到您期望得到的结果:
>>> import socket
>>> import time
>>>
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
>>> starttime = time.time()
>>> try:
... m = sock.recv(100, socket.MSG_DONTWAIT)
... except BlockingIOError as e:
... pass
...
>>> endtime = time.time()
>>> print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s')
sock.recv(100, socket.MSG_DONTWAIT) took 0.0007114410400390625s
如果我“忘记”排除sock.settimeout(0.5)
,我会在socket.timeout
之后得到一个异常0.5s
:
>>> import socket
>>> import time
>>>
>>> sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
>>> sock.settimeout(0.5) # <= see this
>>>
>>> starttime = time.time()
>>> try:
... m = sock.recv(100, socket.MSG_DONTWAIT)
... except socket.timeout as e:
... pass
...
>>> endtime = time.time()
>>>
>>> print(f'sock.recv(100, socket.MSG_DONTWAIT) took {endtime-starttime}s')
sock.recv(100, socket.MSG_DONTWAIT) took 0.501746416091919s
关于“2。是否有更好的方法来切换阻塞和非阻塞调用recv()
”:根据您的应用程序的需要,您可能需要查看select (以及套接字编程中的“非阻塞套接字”部分HOWTO和这个)
推荐阅读
- sql - 从 CASE WHEN 转换为 INT64
- visual-studio-code - 在 webview 中使用 VSCode 文本编辑器?
- mongodb - 带有宏的值类的 Mongo 编解码器
- java - Android Q - 删除媒体(音频)文件
- typescript - Graphql Nestjs 将参数添加到类型字段
- c# - 如何在 EntityFramework 中添加相同的表名但不同的架构?
- python - tsv 文件之间的完全连接和基于原始文件的列重命名
- stata - 如何在Stata中匹配样本(CEM)的面板回归中运行随机效应?
- python - 事件发生后如何使页面重定向到同一页面
- mfc - 单击子控件时如何将对话框置于顶部