我正在尝试编写 perl TCP 服务器/python TCP 客户端,我现在有这样的代码:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ("127.0.0.1", 9000)
sock.connect(server_address)
try:
message = unicode('Test')
sock.sendall(message)
data = sock.recv(1024)
print data
finally:
sock.close()
我注意到,我的 TCP 服务器(用 Perl 编写)不是在 sendall(message)
之后获取消息,而是在 close()
之后获取消息。服务器的工作方式类似于回显服务器,在收到消息后将数据发送给客户端。这会导致死锁,服务器永远不会收到消息,客户端永远不会收到响应。可能有什么问题?在close()
期间,该消息到达服务器时会发生什么?
最佳答案
我大胆猜测这是由于服务器的实现造成的。编写 echo 服务器的方法有很多种:
- 在循环(或异步回调)中接收字节,直到 EOF;当收到字节时(每次循环迭代),回显它们而不进行任何处理或缓冲;当发现 EOF(入站流关闭)时,关闭出站流
- 一次读取行(假设是文本协议(protocol)),即寻找CR/LF/EOF;当找到一行时,返回该行 - 当找到 EOF 时(入站流关闭),关闭出站流
- 读取至 EOF;然后返回所有内容并关闭出站流
如果回显服务器使用第一种方法,它已经按预期工作 - 所以我们可以打折扣。
对于第二种方法,您正在发送文本,但没有 CR/LF,并且您还没有关闭从客户端到服务器的流(EOF),因此服务器永远不会回复此请求。所以是的,它会陷入僵局。
如果是第三种方法,那么,除非您关闭出站流,否则将会死锁。
从你的回答来看,看起来添加一个 \n
“修复”它。由此,我得出结论,您的回显服务器是基于线路的。因此,有两个解决方案,第三个解决方案适用于任何场景:
- 使回显服务器响应原始数据,而不是线路
- 添加行尾标记
- 关闭客户端的出站流,即客户端到服务器的流(许多网络 API 允许您分别关闭出站和入站流)
此外:确保 Nagle 被禁用(通常称为 NO_DELAY) - 这将防止字节在客户端停留一段时间,等待被组成一个合适大小的数据包(这适用于 1 和 2,但不适用于 3;具有启用 Nagle 会增加延迟,但通常不会导致死锁)。
关于python tcp socket - 为什么仅在 close() 之后发送 sendall 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12997562/