英文:
Python socket non-blocking recv() exception(s) and sendall() exception
问题
-
当
recv()
在没有可读取数据时返回时,会引发哪些异常? -
当
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()
我的主要关注点是:
当sendall()
失败时,我会得到什么socket.error.errno
?
如果从失败的sendall()
中获得的socket.error.errno
是EAGAIN
或EWOULDBLOCK
,那将会很困扰!另外,当处理非阻塞的recv()
时,我是否需要检查EAGAIN
和EWOULDBLOCK
两者?
如有需要,我可以提供进一步的信息或解释。
英文:
I'm writing a simple TCP relay server, which is gonna be deployed both on a Windows and a Linux machine (same code base).
Naturally there're gonna be two sockets to work with.
I would like to know which exceptions exactly do get raised for the following cases:
recv()
returns when no data is available to read.sendall()
cannot complete (dispose of the whole to-send data)
- Do I have to check for both errnos (
socket.EWOULDBLOCK
andsocket.EAGAIN
) when expecting to return from a non-blocking socketsrecv()
? - Which
errno
(.args[0]
) do I get whensendall()
fails?
Here's my code so far:
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()
My main concern is:
What socket.error.errno
do I get when sendall()
fails?
Lest the socket.error.errno
I get from a failing sendall()
is EAGAIN
or EWOULDBLOCK
, in which case it'd be troubling!!
Also, do I have to check for both EAGAIN
and EWOULDBLOCK
when handling a non-blocking recv()
?
答案1
得分: 1
以下是您要翻译的内容:
深入探讨
在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的socket模块实现没有错误,那应该是不可能的,但其余的错误都是有效的,包括EAGAIN
和EWOULDBLOCK
。您可以查阅send
可能产生的错误列表的man页面,或查看此在线列表。
至于socket.recv
,实现在sock_recv_impl中,它直接调用recv
,导致类似的情况。再次查看man页面,或在线。
简而言之,对于recv()
,您确实需要处理EAGAIN
和EWOULDBLOCK
,以应对无可用数据的情况,除非您了解您的平台,可以确定它会引发哪个错误。对于sendall()
,您需要处理的错误将取决于您想要支持/从中恢复的错误。作为一个极端的例子-如果您的内存用尽,它会尝试满足发送请求,但这是一个极不可能的情况,那时您可能已经有更大的问题。
XY问题
幸运的是,在您的情况下,有一个简单的解决方案,因为您实际上只关心区分非阻塞接收的预期错误和接收或发送期间的实际错误。只需将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: # 仅在没有异常时运行
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: # 仅在没有异常时运行
if sock2_data:
socket1.sendall(sock2_data)
英文:
A Deep Dive
In CPython, socket.sendall
is implemented by sock_sendall in socketmodule.c, which calls sock_send_impl indirectly via sock_call_ex (as does socket.send
too). Ultimately, this results in calling the system call send(...)
, which will set errno accordingly. sock_call_ex
will handle some of the potential errors- it may automatically retry on EINTR
, and for EWOULDBLOCK
and EAGAIN
it will retry if there's a positive timeout.
Apart from that, however, socket.sendall
can by and large produce most of the same errors that send
can. There are a few, related to malformed arguments that should be impossible, assuming no bugs in python's socket module implementation, but the rest are valid, including EAGAIN
and EWOULDBLOCK
. You can consult the man pages for a list of errors that send
can produce, or see this online list.
As for socket.recv
, the implementation is at sock_recv_impl, which calls recv
directly, resulting in a similar situation. Again, see the man pages, or online.
In short, for recv()
, you indeed need to handle both EAGAIN
and EWOULDBLOCK
for no data available, unless you know your platform so you can determine which it will raise. For sendall()
, the errors you need to handle will depend on the errors you want to support / recover from. As an extreme example- you could get ENOMEM
if you ran out of memory as it tries to fulfill the send request, however that's an extremely unlikely scenario, and you would already have bigger problems at that point.
XY Problem
Luckily, in your case there's an easy solution, since you're really just concerned with distinguishing the expected errors on nonblocking recv from actual errors during either recv or send. Just move the socket.sendall
out of the try
body, using the else
construct:
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)
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论