gevent tcp套接字已断开连接但可以发送数据

gevent tcp socket is disconnected but can send data

我有一个使用 gevent 编写的小型 HTTP 服务器,它接受传入连接并使用每个连接的 greenlet 发回数据。当(本地)客户端死机时,尽管套接字处于 CLOSE_WAIT 状态,但服务器端对客户端的写入成功:

$ ls -l /proc/21860/fd |grep 22
lrwx------. 1 mathieu mathieu 64 Mar 21 19:55 22 -> socket:[187093]
$ lsof |grep 187093
python    21860       mathieu   22u     IPv4             187093       0t0        TCP localhost.localdomain:36072->localhost.localdomain:48908 (CLOSE_WAIT)

现在,我希望我用 gevent.socket.socket 创建的套接字的发送方法失败并出现异常,但我得到 none !

def send(socket, data):
    sent = socket.send(data)
    while sent != len(data):
        data = data[sent:]
        sent = socket.send(data)

我的期望不正确吗?如果不是,这里可能出了什么问题?

经过一些调试,事实证明我对套接字 API 在 TCP 之上的行为方式的预期是不正确的。潜在读者可能需要仔细阅读 TCP option SO_LINGER (zero) - when it's required

带走的东西:

  1. 客户端close()发送FIN给服务器,大致意思是"I am done sending you data"。这意味着服务器可能仍然能够发回数据。
  2. 如果服务器在客户端关闭后尝试写入,写入将成功(returns 从用户空间复制到内核空间的字节数),数据将发送到客户端,客户端将发送回 RST,因为它的套接字仍处于 TIMEWAIT 状态(除非你用 SO_LINGER 零禁用它,这就是为什么你真的不想将 SO_LINGER 设置为零),服务器套接字状态将被更新即将关闭。
  3. 如果服务器在收到来自客户端的 RST 后尝试第二次写入,写入将失败。
  4. 如果服务器在客户端关闭后尝试接收,则接收将 return 为零以指示流结束。

总而言之,这意味着我的服务器需要使用流结束通知 returned as recv() == 0 作为客户端已关闭其连接并采取行动的标志相应地(即关闭关联的套接字)。