twisted:在写入传输前测试连接是否存在
在执行 transport.write()
之前,有没有办法测试一下连接是否还存在呢?
我修改了 simpleserv/simpleclient 的示例代码,让它每5秒发送一次消息(写入 Protocol.transport
)。这个连接是持续的。
当我断开 WiFi 时,它仍然会写入 transport(当然消息不会到达另一端),但没有报错。重新启用 WiFi 后,消息会被送达,但在 下一次尝试 发送消息时就失败了(这时会调用 Protocol.connectionLost
)。
下面是事情发生的顺序:
- 发送消息时建立连接,消息成功送达。
- 关闭 WiFi
- 发送消息写入
transport
,没有报错,但消息没有到达 - 重新开启 WiFi
- 第三步发送的消息成功送达
- 发送消息时调用了
Protocol.connectionLost
在执行第6步之前,知道我是否可以写入 transport 会很不错。有办法做到吗?
服务器:
# 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 个回答
Protocol.connectionLost
是唯一能让你知道连接已经断开的方式。它会在确认连接不再存在的最早时刻被调用。
对于你我来说,断开网络适配器(比如,关闭你的WiFi卡)会导致连接中断,这一点很明显——至少,如果你一直保持关闭状态,或者在重新开启时配置不同的话。不过,对于你所使用的平台的TCP实现来说,这并不明显。
因为网络通信不是瞬间完成的,任何单独的数据包可能因为正常(非致命)原因而丢失,所以TCP会包含各种超时和重试机制。当你断开网络适配器时,这些数据包就无法再送达,但平台并不知道这种情况会持续超过最长的TCP超时时间。因此,当你关闭WiFi时,TCP连接并不会立即关闭。它会继续存在,并开始重试发送和等待确认。
最终,所有的超时和重试都会到期,连接才会真正关闭(不过TCP的工作方式意味着如果没有数据等待发送,那么实际上是没有超时的,一个“死”的连接会永远存在;解决这个问题的原因就是TCP的“保持活动”功能)。这变得稍微复杂一些,因为连接的两边都有超时。如果在第六步写入时连接立即关闭(而不是更早),那么原因可能是一个“重置”(RST
)数据包。
重置会在连接另一端的超时到期后发生,这时连接在你这边仍然是打开的。现在,当你这边为这个TCP连接发送数据包时,另一边不会识别这个连接(因为对另一边来说,这个连接已经不存在了),于是会回复一个重置消息。这告诉最初的发送者没有这样的连接。最初的发送者会因此关闭自己这边的连接(因为单方面的连接并没有太大用处)。这大概就是在你的应用中调用Protocol.connectionLost
的时机。
这一切基本上就是TCP的工作原理。如果超时行为不适合你的应用,你有几个选择。你可以开启TCP保持活动功能(这通常没有太大帮助,默认情况下TCP保持活动会引入几个小时的超时,不过你可以在大多数平台上进行调整),或者你可以构建一个应用层的保持活动功能。这只是你的协议生成的一些额外流量,然后期待一个响应。你可以在此基础上建立自己的超时机制(比如3秒内没有响应?关闭连接并重新建立一个)或者仅仅依赖它来触发一个稍微快一点的(大约2分钟)TCP超时。更快的超时的缺点是,偶发的网络问题可能会导致你在实际上并不需要关闭连接的情况下关闭它。