我正在编写一个简单的 TCP 中继服务器,它将部署在 Windows 和 Linux 机器上(相同的代码库)。 自然会有两个插座可以使用。 我想知道在以下情况下确实会引发哪些异常:
recv()
在没有数据可读时返回。sendall()
无法完成(处理整个发送数据)socket.EWOULDBLOCK
和 socket.EAGAIN
)recv()
?errno
失败时我得到哪个.args[0]
(sendall()
)?到目前为止,这是我的代码:
try:
socket1.setblocking(False)
socket2.setblocking(False)
while True:
try:
sock1_data = socket1.recv(1024)
if sock1_data:
socket2.sendall(sock1_data)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
try:
sock2_data = socket2.recv(1024)
if sock2_data:
socket1.sendall(sock2_data)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
except:
pass
finally:
if socket2:
socket2.close()
if socket1:
socket1.close()
我主要关心的是: 当
socket.error.errno
失败时,我会得到什么 sendall()
?
唯恐失败的
socket.error.errno
得到的sendall()
是EAGAIN
或EWOULDBLOCK
,那样的话就麻烦了!!
此外,在处理非阻塞
EAGAIN
时,我是否必须同时检查EWOULDBLOCK
和recv()
?
在 CPython 中,
socket.sendall
由 socketmodule.c 中的 sock_sendall 实现,它通过 sock_call_ex 间接调用 sock_send_impl(socket.send
也是)。最终,这会调用系统调用send(...)
,这将相应地设置errno。 sock_call_ex
将处理一些潜在的错误 - 它可能会在 EINTR
时自动重试,对于 EWOULDBLOCK
和 EAGAIN
如果有正超时它会重试。
然而,除此之外,
socket.sendall
大体上可以产生与 send
相同的大部分错误。假设 python 的套接字模块实现中没有错误,有一些与格式错误的参数有关,应该是不可能的,但其余的都是有效的,包括 EAGAIN
和 EWOULDBLOCK
。您可以查阅手册页以获取 send
可能产生的错误列表,或者 查看此在线列表。
至于
socket.recv
,实现在sock_recv_impl,直接调用recv
,导致类似的情况。再次查看手册页,或 online.
简而言之,对于
recv()
,您确实需要同时处理EAGAIN
和EWOULDBLOCK
没有可用数据,除非您了解您的平台以便确定它会筹集哪些资金。对于sendall()
,你需要处理的错误将取决于你想要支持/恢复的错误。举一个极端的例子——如果你在尝试完成发送请求时内存不足,你可能会得到ENOMEM
,但这是一种极不可能的情况,那时你已经遇到了更大的问题。
幸运的是,在您的情况下有一个简单的解决方案,因为您实际上只关心区分非阻塞接收的预期错误与接收或发送期间的实际错误。只需使用
socket.sendall
构造将 try
移出 else
主体:
while True:
try:
sock1_data = socket1.recv(1024)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
else: # only runs if there was no exception
if sock1_data:
socket2.sendall(sock1_data)
try:
sock2_data = socket2.recv(1024)
except socket.error as e:
if e.args[0] != socket.EAGAIN and e.args[0] != socket.EWOULDBLOCK:
raise e
else: # only runs if there was no exception
if sock2_data:
socket1.sendall(sock2_data)