Повторные передачи TCP вызывают тайм-ауты во время разговора XML между клиентом Ubuntu (curl) и сервером - PullRequest
0 голосов
/ 31 марта 2020

Я пытаюсь выяснить и устранить причину повторных передач ACK FIN, показанную на скриншоте ниже. Я использую сервер Ubuntu 18 apache с PHP, который взаимодействует с XML поставщиком услуг через PHP curl (PHP 7.3). Во время разговоров XML рассматриваемый сервер, по-видимому, отключается через произвольные интервалы. Это влияет на сервис xml либо на тайм-аут, либо на возврат неполного xml набора результатов. Другой разработчик сообщил мне, что у них были похожие проблемы, и прибегнул к PHP l oop, чтобы повторить неудачные запросы несколько раз, пока служба не ответит правильно (из их собственных клиентских блоков разработчика). Это указывает на теорию, что что-то может быть не так на стороне сервера, однако я не могу развлечь это, пока не исключу все возможности с моей стороны (что, если другие разработчики использовали ту же клиентскую ОС, что и я? Et c)

Я много тестировал у почтальона на моей локальной машине windows, однако я не могу получить тайм-ауты / ошибки, которые могут возникнуть здесь. Не уверен, есть ли у почтальона какой-то механизм исправления ошибок или сам мой windows, кажется, лучше взаимодействует со стеком TCPIP серверного компьютера.

То, что я пробовал до сих пор: - сбил соединение с https на http, чтобы я мог перехватить с помощью wireshark на клиенте Ubuntu (https как причина, исключенная в то же время) - изменил MTU с 9001 (ec2 например) до 1500 и 1492 соответственно, проблема все еще существует, MTU сервисного сервера, по-видимому, равен 1500 - включены keepalive на curl, безрезультатно - попытка разных таймаутов и таймаутов соединения в настройках curl, безрезультатно - попытка того же l oop in php, чтобы повторить запрос. добавлен флаг, чтобы полностью разорвать tcp-соединение, если повторная попытка в пределах curl не имеет никакого эффекта, повторные запросы по-прежнему будут время ожидания, в других случаях они возвращают то, что ожидается в xml. Казалось бы, случайно. Из 20 запросов может произойти сбой 2.

примечания: Похоже, что после того, как клиент отправляет запрос xml после отправки, сервер отвечает ACK, но никогда не отправляет статус 200, так как я получил это, когда запрос не удался , По-видимому, это приводит к повторным повторным передачам FIN ACK от клиента, здесь, похоже, происходит некоторое исправление ошибок, однако это не пузыривается до уровня XML для представления полного запроса, вместо этого CURL выдает тайм-аут, пока он ожидает ответа. В wireshark я мог видеть неполный ответ, ie около половины xml, восстановленный в строке 42. Мои единственные предположения, что, возможно, сервер представляет собой ящик windows, который может каким-то образом быть несовместимым со стеком tcp ip в Ubuntu, или что это просто ошибка на сервере, которая не может быть исправлена ​​независимо от того, что я делаю, кроме повторных запросов.

Есть идеи? Я не эксперт по TCPIP, поэтому FINS и ACKS понятны только :) Что бы вы попробовали дальше?

wireshark screenshot

1 Ответ

0 голосов
/ 07 апреля 2020

Существует много данных, которые можно декодировать из захвата сети. Во-первых, обратите внимание, что кадры 46 и 47 (начало последующего TCP-соединения, поскольку мы не видим начала рассматриваемого соединения) согласовывают максимальный размер сегмента (MSS), равный 1460. Однако кадр 53 имеет 4380 байтов (3 раза). MSS) и кадр 55 имеет 7300 байтов (5-кратное MSS). Это наиболее вероятно, потому что захват сети был сделан на клиентском хосте, и некоторая форма разгрузки приема включена на NI C или драйвере. В общем случае лучше всего захватывать сеть вне сети (например, подключая порт коммутатора и получая захват от NI C на объединенном порту в случайном режиме). Так что имейте в виду, что то, что мы видим в захвате, не совсем то, что было на проводе.

Мы видим, что рассматриваемый запрос отправляется в кадре 34, а затем мы видим его подтвержденным (с последовательностью 510) через 11 мс. .

Тогда в течение примерно 20 секунд ничего не происходит. На уровне TCP это совершенно нормально. Насколько мы можем судить, не передаются неподтвержденные данные (таким образом, TCP не закрывает соединение, а тайм-аут пользователя TCP не работает, даже если он включен), так что это соединение может бездействовать в течение некоторого времени, пока TCP не будет активен. in (если включено).

Затем в кадре 36 клиент отправляет FIN с последовательностью 510. На уровне TCP это указывает, что клиент завершил отправку, что вызвано приложением, вызывающим close() или shutdown() на розетке. Поскольку это время составляет почти 20 секунд, оно похоже на тайм-аут на уровне приложения, хотя оно не является тайм-аутом по умолчанию для PHP Curl, но тогда вы явно возились с различными настройками.

Теперь мы ожидаем, что сервер подтвердит отправленный FIN и отправит все данные, которые ему необходимо отправить, а затем, когда серверное приложение вызовет close() или shutdown(), FIN будет отправлено с сервера.

Однако сервер не ACK FIN клиента, и стек TCP клиента повторно передает FIN 5 раз. Вы можете видеть, что время повторной передачи удваивается после каждого FIN, с ожидаемой шестой повторной передачей около 31,38 секунд, при условии, что она не сдавалась. На Linux я полагаю, что это контролируется tcp_orphan_retries, который по умолчанию равен 8, поэтому я думаю, что он не сдался.

Наконец, в кадре 42 сервер говорит, задолго до ожидается повторная повторная передача FIN. Это ACK-последовательность 511, указывающая, что она получила один или несколько FIN. И он содержит полную полезную нагрузку MSS, что на уровне протокола просто отлично.

Итак, теперь вопрос в том, почему стек TCP сервера не принимал FIN до сих пор? И является ли полная полезная нагрузка MSS повторной передачей или это первая отправка? Давайте пока остановимся на догадках, так как в захвате есть дополнительная полезная информация.

Клиент немедленно отвечает RST. Это связано либо с тем, что TCP отказался от сервера, и соединение больше не существует, либо из-за того, что приложение вызвало close() в сокете, поэтому стек TCP не может доставить данные клиентскому приложению и все надеются на корректное завершение работы ушел Я предполагаю, что это последнее.

Затем, на удивление, в кадре 44 мы получаем FIN с сервера с последовательностью 16061 . * * WAT тысяча двадцать-семь ??? . Кадр 42 отправил первые 1460 байтов ответа, поэтому отсутствуют 16060-1460 байтов данных, что составляет 10 MSS. Здесь явно потеря пакетов (именно поэтому WireSharks аннотировал пакет с помощью «Предыдущий сегмент не захвачен»).

Я не думаю, что FIN является повторной передачей, потому что есть много неподтвержденных данные, подлежащие повторной передаче (стоимостью 11 MSS), и эти значения FIN, как правило, передаются в close() даже с непогашенными непогашенными данными. Я предполагаю, что этот FIN был отправлен, когда сервер вызвал close() в сокете.

Так что, если FIN не является повторной передачей, я также предполагаю, что первые 1460 байтов, полученных в кадре 42 всего 10 мс ранее, вероятно, также не так хороши. Я думаю, что сервер задумался, записал данные 11 MSS в сокет и закрыл сокет, вызывая 11 пакетов с полезной нагрузкой (по общему признанию, все не распакованы, но современная реализация / конфигурация TCP "медленный запуск" позволила бы за этим) следовал FIN, но только два из них сделали это из-за некоторой формы потери пакета. Кроме того, я предполагаю, что стек TCP, вероятно, принимал несколько FIN, но также был подвержен потере пакетов.

Поэтому я предлагаю увеличить время ожидания для PHP Curl. Мое предположение заключается в том, что серверу потребовалось более 20 секунд для вычисления его ответа, а время ожидания потери пакетов и повторной передачи приведет только к дальнейшим задержкам.

...