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

时间:2014-03-02 08:55:21

标签: python twisted twisted.internet

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

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

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

这里再次按时间顺序发生:

  1. 发送消息建立连接,消息传递。
  2. 禁用wifi
  3. 发送邮件写入transport,不会抛出错误,邮件未到达
  4. 启用wifi
  5. 3.发送的消息到达
  6. 发送邮件会导致Protocol.connectionLost来电
  7. 如果我可以写入传输,那么在执行步骤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()
    

1 个答案:

答案 0 :(得分:9)

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

对您或我来说显而易见的是,断开网络适配器(即关闭wifi卡)会断开连接 - 至少,如果您将其关闭,或者在重新打开时将其配置为不同。但是,平台的TCP实现并不明显。

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

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

在连接另一端的超时到期后将发生重置,并在连接仍然打开时关闭连接。现在,当您的一方发送此TCP连接的数据包时,另一方将无法识别其所属的TCP连接(因为只要另一方担心连接不再存在)并使用重置消息进行回复。这告诉原始发件人没有这样的连接。原始发送者通过关闭其连接侧来对此作出反应(因为双侧连接的一侧本身并不是非常有用)。这可能是在您的应用程序中调用Protocol.connectionLost时。

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