Существует много данных, которые можно декодировать из захвата сети. Во-первых, обратите внимание, что кадры 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 секунд для вычисления его ответа, а время ожидания потери пакетов и повторной передачи приведет только к дальнейшим задержкам.