Skip to main content
 首页 » 编程设计

python之twisted:在写入传输之前测试连接是否存在

2025年05月04日10落叶无声

是否有可能在执行 transport.write() 之前测试连接是否仍然存在?

我修改了 simpleserv/simpleclient 示例,以便每 5 秒发送一条消息(写入 Protocol.transport)。连接是持久的。

当断开我的 wifi 时,它仍然写入传输(当然消息不会到达另一端)但不会抛出错误。 再次启用 wifi 时,正在传递消息,但下一次发送消息的尝试失败(并且调用了 Protocol.connectionLost)。

这里又按时间顺序发生了什么:

  1. 发送消息建立连接,消息被传递。
  2. 禁用无线网络
  3. 发送消息写入transport,不抛出错误,消息没有到达
  4. 启用无线网络
  5. 3 中发送的消息到达
  6. 发送消息导致 Protocol.connectionLost 调用

在执行第 6 步之前知道我是否可以写入传输会很高兴。有什么办法吗?

服务器:

# Copyright (c) Twisted Matrix Laboratories. 
# See LICENSE for details. 
 
 
from twisted.internet import reactor, protocol 
 
 
class Echo(protocol.Protocol): 
    """This is just about the simplest possible protocol""" 
 
    def dataReceived(self, data): 
        "As soon as any data is received, write it back." 
        print 
        print data 
        self.transport.write(data) 
 
def main(): 
    """This runs the protocol on port 8000""" 
    factory = protocol.ServerFactory() 
    factory.protocol = Echo 
    reactor.listenTCP(8000,factory) 
    reactor.run() 
 
# this only runs if the module was *not* imported 
if __name__ == '__main__': 
    main() 

客户:

# Copyright (c) Twisted Matrix Laboratories. 
# See LICENSE for details. 
 
 
""" 
An example client. Run simpleserv.py first before running this. 
""" 
 
from twisted.internet import reactor, protocol 
 
# a client protocol 
 
counter = 0 
 
class EchoClient(protocol.Protocol): 
    """Once connected, send a message, then print the result.""" 
 
    def connectionMade(self): 
        print 'connectionMade' 
 
    def dataReceived(self, data): 
        "As soon as any data is received, write it back." 
        print "Server said:", data 
 
    def connectionLost(self, reason): 
        print "connection lost" 
 
    def say_hello(self): 
        global counter 
        counter += 1 
        msg = '%s. hello, world' %counter 
        print 'sending: %s' %msg 
        self.transport.write(msg) 
 
class EchoFactory(protocol.ClientFactory): 
 
    def buildProtocol(self, addr): 
        self.p = EchoClient() 
        return self.p 
 
    def clientConnectionFailed(self, connector, reason): 
        print "Connection failed - goodbye!" 
 
    def clientConnectionLost(self, connector, reason): 
        print "Connection lost - goodbye!" 
 
    def say_hello(self): 
        self.p.say_hello() 
        reactor.callLater(5, self.say_hello) 
 
# this connects the protocol to a server running on port 8000 
def main(): 
    f = EchoFactory() 
    reactor.connectTCP("REMOTE_SERVER_ADDR", 8000, f) 
    reactor.callLater(5, f.say_hello) 
    reactor.run() 
 
# this only runs if the module was *not* imported 
if __name__ == '__main__': 
    main() 

请您参考如下方法:

Protocol.connectionLost 是了解连接何时不再存在的唯一方法。当已知连接不再存在时,它也会在最早时间被调用。

很明显,断开您的网络适配器(即关闭您的 wifi 卡)会断开连接 - 至少,如果您将其关闭或再次打开时配置不同.不过,对于您平台的 TCP 实现而言,这并不明显。

由于网络通信不是即时的,任何单个数据包都可能因正常(非致命)原因而丢失,因此 TCP 包括各种超时和重试。当您断开网络适配器时,这些数据包将无法再传送,但平台不知道这种情况会超过最长的 TCP 超时。因此,当您关闭 wifi 时,您的 TCP 连接不会关闭。它挂起并开始重试发送并等待确认。

在某些时候超时和重试都会过期并且连接确实会关闭(尽管 TCP 的工作方式意味着如果没有数据等待发送那么实际上没有超时,“死”连接将永远存在;解决这个问题是 TCP“保持事件”功能存在的原因)。由于连接的两侧 都存在超时,因此这会稍微复杂一些。如果连接在您执行第六步中的写入操作后立即关闭(而且很快),则原因可能是“重置”(RST) 数据包。

连接另一端的超时到期后将发生重置并关闭连接,而您这边的连接仍处于打开状态。现在,当您为此 TCP 连接发送数据包时,另一方将无法识别它所属的 TCP 连接(因为就另一方而言,连接不再存在)并回复一条重置消息。这告诉原始发件人没有这样的连接。原始发送者对此作出 react ,关闭其一侧的连接(因为两侧连接的一侧本身并不是很有用)。这大概是在您的应用程序中调用 Protocol.connectionLost 时发生的。

所有这些基本上就是 TCP 的工作原理。如果超时行为不适合您的应用程序,那么您有几个选择。您可以打开 TCP keepalive(这通常没有帮助,默认情况下 TCP keepalive 会引入数小时的超时,但您可以在大多数平台上对其进行调整)或者您可以构建应用程序级别的 keepalive 功能。这只是您的协议(protocol)生成然后期望响应的一些额外流量。您可以在此基础上构建自己的超时(3 秒内无响应?关闭连接并建立新连接),或者仅依靠它来触发速度稍快(约 2 分钟)的 TCP 超时之一。更快超时的缺点是虚假网络问题可能会导致您在确实不需要时关闭连接。