1

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

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

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

这里又是按时间顺序发生的事情:

  1. 发送消息建立连接,消息被传递。
  2. 禁用 wifi
  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()
4

1 回答 1

10

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

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

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

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

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

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

于 2014-03-02T13:01:43.590 回答