2016-04-29 7 views
1

У меня проблемы с моим серверным приложением, написанным на Python3/asyncio (Protocol), но я уверен, что это не так много python или asyncio, потому что я пробовал другую версию и некоторые 5liner только с интерфейсом сокета. Речь идет о параллельной связи со многими устройствами для клиентов TCP/IP < -> Преобразователи RS232. Именно по этой причине используется asyncio, а не потоки с блокировкой write.Python: TCP сломанный маршрут мучительно медленный для обнаружения

Существует некоторая периодическая передача коротких данных. Проблема возникает, когда я физически разорвите соединение и ждать исключение происходит:

asyncio - Fatal read error on socket transport protocol 
<_SelectorSocketTransport fd=11 read=polling write=<idle, bufsize=0>> 
Traceback (most recent call last): 
File "/usr/lib/python3.5/asyncio/selector_events.py", line 663, in 
_read_ready 
data = self._sock.recv(self.max_size) 
OSError: [Errno 113] No route to host 

это происходит, но после 15 минут, что означает, что я сигнализации в течение 15 минут все в порядке, но это ISN» t, который невыносимо длинный и нарушает функцию. Поведение проверено в Ubuntu 16.04, Ubuntu 14.04 и Debian Jessie, все в разных HW.

Я обнаружил, что (вероятно) ядро ​​буферизует данные, потому что, если я заново подключу устройство через десять минут, все данные будут красными сразу. Я понимаю, что это хорошо для короткого разъединения, у меня не было бы проблем с 10, 15 или даже минутой, но 15 минут слишком много.

На подобный вопрос ответил исполнительный протокол приложения, что невозможно в моем случае. Я просто хочу быть уверенным, что другая сторона получит пакет (TCP ack) в разумные сроки. Я внимательно прочитал документы о socket.setsockopt, но не нашел ничего полезного. Также не нашел способ проверить, был ли сброшен буфер отправки, чтобы сделать некоторые обходные пути - ручное обнаружение неисправного маршрута.

TCP keep-alive также не помогает, поскольку он основан на времени бездействия и отправке данных - это активность.

ответ

0

Вы видите поведение тайм-аута повторной передачи TCP (RTO).

Ваш TCP никогда не получает никакой обратной связи¹, поэтому он очень сильно пытается перебрать сегменты. В Linux это поведение регулируется net.ipv4.tcp_retries2 = 15:

Это значение влияет на тайм-аут живой связи с TCP, когда RTO повторные передачи остаются неподтвержденными. Учитывая значение N, гипотетическое TCP-соединение после экспоненциального отсрочка с исходным RTO с помощью TCP_RTO_MIN будет повторно передавать N раз, прежде чем убить соединение на (N + 1) -м RTO.

Значение по умолчанию 15 дает гипотетическое тайм-аут 924,6 секунд и является нижней границей для эффективного тайма-аута. TCP будет эффективно тайм-аут на первом RTO, который превышает гипотетический таймаут.

Что это означает, что ваш send по-видимому, работает (т.е. TCP согласился отправить данные в конечном счете) и ~ 900 секунд вы ждете TCP, чтобы попытки.

Изменение протокола приложения - это надежный способ исправить это, но поскольку вы упоминаете, что он не работает для вас, ваши варианты вращаются вокруг запроса TCP.

TCP_USER_TIMEOUT, кажется, делает именно то, что вы хотите:

Если значение больше 0, то определяет максимальное количество времени в миллисекундах, передаваемые данные могут оставаться непризнанным до того TCP будет принудительно закройте соответствующее соединение и введите ETIMEDOUT в приложение.

Дополнительная информация о Application Control of TCP retransmission.

Также не нашел способ проверить, был ли сброс буфера отправки, чтобы сделать некоторые обходные пути - ручное обнаружение неисправного маршрута.

Вопрос, связанный выше, имеет SIOCOUTQ - проверка количества данных в выходной очереди - как обходной путь, который вы описываете.


¹ Например, он может получать TCP RST или ICMP недоступный.