Python 套接字非阻塞 recv() 异常和 sendall() 异常

问题描述 投票:0回答:1

我正在编写一个简单的 TCP 中继服务器,它将部署在 Windows 和 Linux 机器上(相同的代码库)。 自然会有两个插座可以使用。 我想知道在以下情况下确实会引发哪些异常:

  1. recv()
    在没有数据可读时返回。
  2. 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()

python sockets exception nonblocking
1个回答
0
投票

深潜

在 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
,但这是一种极不可能的情况,那时你已经遇到了更大的问题。

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:  # 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)
© www.soinside.com 2019 - 2024. All rights reserved.