是否有可能在执行之前测试连接是否仍然存在transport.write()
?
我修改了simpleserv/simpleclient示例,以便Protocol.transport
每隔5秒发送一次消息(写入).连接是持久的.
当断开我的wifi时,它仍然写入传输(当然消息不会到达另一端)但不会引发错误.当再次启用wifi时,正在传递消息,但下一次发送消息的尝试失败(并且Protocol.connectionLost
被调用).
这里再次按时间顺序发生:
发送消息建立连接,消息被传递.
禁用wifi
发送消息写入transport
,不会抛出错误,消息不会到达
启用wifi
3.发送的消息到达
发送消息会导致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()
Jean-Paul Ca.. 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超时之一.更快超时的缺点是虚假的网络问题可能会导致您在真正不需要时关闭连接.