我在linux / mac上,在远程和本地机器上使用python2.7和python3.5测试了以下代码:
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')
答案 0 :(得分:4)
因为你的python代码所代表的客户端还不知道服务器的套接字是关闭的并且已经消失了。将TCP连接视为两个独立的通道:每个方向一个。 TCP协议允许每一方在其自己选择的时间关闭其连接的发送侧。并且TCP无法向另一方表明将来不会接受将来向另一方向发送的信息。
更详细地说......普通终止TCP会话涉及每个侧发送一个设置了FIN
标志的数据包。这表明发送FIN
的对等方不会再发送任何数据。 not 表示FIN
数据包的接收方可能不再发送任何数据。
所以,这里发生的事情是当你杀死netcat
时,一个FIN
数据包从服务器端发送到客户端(并由网络堆栈代表客户端确认)。这将关闭套接字的server =>客户端方向。但是,只要客户端知道,client =>服务器方向仍然可用。稍后,当您的客户端尝试发送数据时,将发送包含数据的数据包。现在,服务器端的网络堆栈立即响应,通过发送RST
数据包告诉客户端服务器不再存在。但是,您的sendall
函数调用会在收到的时间内完成。
因此,如果您的客户端再睡一秒钟(或者实际上只是一小部分时间),然后再尝试另一个发送,那么后续发送会引发异常。
创建整个会话的数据包捕获并使用wireshark进行研究可能是有益的。在调用python代码之前在另一个窗口中运行它(然后使用Ctrl-C终止):
sudo tcpdump -i lo -w /tmp/f.pcap port 8080
您还可以使用wireshark捕获它,指定与捕获过滤器相同的port 8080
- 或tcp.port == 8080
作为显示过滤器。