Витая: проверить, существует ли соединение перед записью в транспорт
Есть ли возможность проверить, существует ли еще соединение перед выполнением transport.write()
?
Я изменил примеры simpleserv / simpleclient так, чтобы сообщение отправлялось Protocol.transport
каждые 5 секунд. Связь постоянна.
При отключении моего wifi он все равно пишет в транспорт (конечно сообщения не приходят на другую сторону), но ошибки не выдается. При повторном включении Wi-Fi сообщения доставляются, но следующая попытка отправить сообщение не удалась (и Protocol.connectionLost
называется).
И здесь, что происходит в хронологическом порядке:
- Отправка сообщения устанавливает соединение, сообщение доставляется.
- Отключение Wi-Fi
- Отправка сообщения пишет в
transport
, не выдает ошибку, сообщение не приходит - Включение Wi-Fi
- Сообщение отправлено в 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()
1 ответ
Protocol.connectionLost
это единственный способ узнать, когда связь больше не существует. Он также вызывается в самое раннее время, когда известно, что соединение больше не существует.
Для вас или для меня очевидно, что отключение сетевого адаптера (т. Е. Отключение Wi-Fi-карты) приведет к разрыву соединения - по крайней мере, если вы отключите его или настроите его иначе, когда снова включите его. Это не очевидно для реализации вашей платформы TCP.
Поскольку сетевое взаимодействие не происходит мгновенно и любой отдельный пакет может быть потерян по обычным (не фатальным) причинам, TCP включает различные таймауты и повторные попытки. Когда вы отключаете сетевой адаптер, эти пакеты больше не могут быть доставлены, но платформа не знает, что это условие продлится самое длительное время ожидания TCP. Таким образом, ваше TCP-соединение не закрывается, когда вы выключаете Wi-Fi. Он зависает и начинает повторять попытку отправки и ждет подтверждения.
В какой-то момент все тайм-ауты и повторные попытки истекают, и соединение действительно закрывается (хотя работа TCP означает, что если нет данных, ожидающих отправки, то на самом деле тайм-аут не существует, "мертвое" соединение будет жить вечно устранение этой причины является причиной наличия функции TCP "keepalive"). Это немного усложняется тем, что на обеих сторонах соединения есть тайм-ауты. Если соединение закрывается, как только вы выполняете запись на шаге 6 (и не раньше), возможно, причиной является "сброс" (RST
) пакет.
Сброс произойдет после истечения времени ожидания на другой стороне соединения и закроет соединение, пока соединение еще открыто на вашей стороне. Теперь, когда ваша сторона отправляет пакет для этого TCP-соединения, другая сторона не распознает TCP-соединение, к которому оно принадлежит (поскольку другая сторона обеспокоена тем, что соединение больше не существует), и отвечает сообщением сброса. Это говорит оригинальному отправителю, что такой связи нет. Исходный отправитель реагирует на это, закрывая свою сторону соединения (поскольку одна сторона двустороннего соединения сама по себе не очень полезна). Это предположительно, когда Protocol.connectionLost
называется в вашем приложении.
Все это в основном так, как работает TCP. Если время ожидания не подходит для вашего приложения, у вас есть несколько вариантов. Вы можете включить TCP keepalive (обычно это не помогает, по умолчанию TCP keepalive вводит тайм-ауты длительностью в несколько часов, хотя вы можете настроить его на большинстве платформ) или вы можете создать функцию keepalive на уровне приложения. Это просто дополнительный трафик, который генерирует ваш протокол, а затем ожидает ответа. Вы можете создать свои собственные тайм-ауты (без ответа в течение 3 секунд? Закрыть соединение и установить новое) поверх этого или просто положиться на него, чтобы вызвать один из несколько более быстрых (~2 минут) тайм-аутов TCP. Недостатком более быстрого тайм-аута является то, что ложные проблемы с сетью могут привести к закрытию соединения, когда вам это действительно не нужно.