【问题标题】:python sendall not raising connection closed errorpython sendall没有提高连接关闭错误
【发布时间】:2018-07-14 17:52:18
【问题描述】:

我有以下代码,用 python2.7 和 python3.5 测试,在 linux / mac 上,在远程和本地机器上:

import socket
import time

s = socket.socket()
s.connect(("127.0.0.1", 8080))
s.sendall(b'first')
time.sleep(20)
s.sendall(b'should fail')

另一方面,我有一个使用netcat -l -p8080 运行的“服务器”

一看到first,我就杀死了netcat命令

但是s.sendall 没有引发任何异常,为什么会这样?

编辑:注意再做一次s.sendall 然后引发异常

import socket
import time

s = socket.socket()
s.connect(("127.0.0.1", 8080))
s.sendall(b'first')
time.sleep(20)
s.sendall(b'should fail')
s.sendall(b'now it really fails')

【问题讨论】:

    标签: python sockets network-programming


    【解决方案1】:

    因为你的python代码所代表的客户端还不知道服务器的套接字已经关闭并消失了。将 TCP 连接视为两个独立的通道:每个方向一个。 TCP 协议允许每一方在自己选择的时间关闭连接的发送方。并且 TCP 没有办法向对方表明将来在另一个方向的发送将不被接受。

    更详细... TCP 会话的普通终止涉及每个 端发送带有FIN 标志集的数据包。这表示发送FIN 的对等方将不再发送任何数据。它确实表明FIN 数据包的接收者可能不再发送任何数据。

    所以,这里发生的情况是,当你杀死netcat 时,一个FIN 数据包从服务器端发送到客户端(并代表客户端由网络堆栈确认)。这将关闭套接字的服务器 => 客户端方向。但是,就客户端所知,client=>server 方向仍然可用。稍后,当您的客户端尝试发送数据时,会发送一个包含数据的数据包。现在,服务器端的网络堆栈通过发送RST 数据包立即响应告诉客户端服务器不再存在。但是,您的 sendall 函数调用在收到时早已完成。

    因此,如果您的客户端再睡一秒钟(或者实际上只是一秒钟的一小部分)然后尝试另一个发送,那么后续发送引发例外。

    创建整个会话的数据包捕获并使用wireshark研究它可能是有益的。在调用你的 python 代码之前在另一个窗口中运行它(然后用 Ctrl-C 杀死):

    sudo tcpdump -i lo -w /tmp/f.pcap port 8080
    

    您也可以使用wireshark 来捕获它,将相同的port 8080 指定为捕获过滤器- 或将tcp.port == 8080 指定为显示过滤器。

    【讨论】:

    • hmmm 是的,如果我有第 3 次“sleep then sendall”这个失败,管道损坏,所以这意味着如果我想测试连接是否关闭,我需要至少调用 2 次“ sendall”(或发送)?
    • 第二次应该没问题,没有任何睡眠(或运气)对吧?因为通话被阻塞了,对吧?
    • 不一定不睡觉。该调用是“阻塞的”——但只有在您的数据被复制到内核网络缓冲区之前,当sendall 调用返回时,它仍然不会完成发送和接收RST 数据包。有几种方法可以检测到对方已经关闭。一种是调用select,将您的套接字作为“读取” FD 之一——这个想法是当另一端关闭时,select 会告诉您套接字变得可读(实际上,调用read/ recv 之后将在套接字上返回文件结尾)。
    猜你喜欢
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2011-06-29
    • 2020-04-24
    • 1970-01-01
    • 2018-12-28
    • 2018-07-11
    相关资源
    最近更新 更多