Размер окна TCP резко увеличивается, и отправитель не отправляет, пока приемный буфер не будет пуст
У меня есть поток загрузки через TCP в приложении (работает на Win2k12).
Проблема в том, что отправитель закрывает соединение, потому что оно истекло.
Я использовал wireshark, чтобы увидеть, что происходит на 2 разных серверах (на одном сервере все работает нормально, на другом время ожидания истекло). Я заметил одинаковое поведение на обоих:
Когда начинается загрузка, все выглядит нормально, размер окна составляет 64 КБ и остается неизменным в течение некоторого времени, сегменты подтверждаются. Затем в какой-то момент размер окна начинает уменьшаться до тех пор, пока он не станет равным 0. (Насколько я знаю, это нормально, получатель не может идти в ногу с отправителем.) Однако, от получателя нет сообщения ACK или обновления окна до тех пор, пока приложение считывает весь буфер, затем обновление окна снова объявляет размер окна 64 КБ. Тогда это начинается снова. Размер окна уменьшается до нуля.
Это не похоже на меня. Поскольку приложение выполняет чтение из буфера, в нем должно быть свободное место, и должно быть отправлено обновление окна, чтобы отправитель мог отправить следующий сегмент.
Другая вещь, которую я не понимаю, это поведение на отказавшем сервере. Этот сервер объявляет о больших и больших размерах окна в каждом таком цикле, в последнем цикле перед тайм-аутом размер окна составлял ~800 000. Тайм-аут происходит из-за того, что буфер очищается недостаточно быстро. Но я понятия не имею, почему размер окна увеличивается на этом сервере? Есть ли настройка на сервере, чтобы предотвратить это?
Верны ли мои предположения, или я что-то неправильно понял в протоколе TCP? Любые идеи для решения этой проблемы приветствуются.
Благодарю.
1 ответ
Если процесс приема не обрабатывает данные так быстро, как они могут быть переданы по сети, предполагается, что окно будет уменьшаться при получении пакетов до тех пор, пока не будет заполнен буфер приема, а окно равно 0. Предполагается, что сервер все еще должен подтверждать полученные данные в этой ситуации, так что отправитель знает, чтобы не передавать его повторно.
После того как окно перешло в 0, принимающая сторона не должна объявлять, что в окне есть место для дополнительных данных сразу после того, как приложение прочитает еще один байт из потока. Он должен, по крайней мере, подождать, пока будет достаточно свободного места для соответствия одному пакету размера MTU. Ожидание намного дольше, чем это не очень хорошая идея.
Динамическое изменение размера памяти во время передачи - разумное поведение. Однако алгоритм должен быть направлен на то, чтобы сходиться на размер, достаточно большой, чтобы не вызывать узкое место, но он не должен быть намного больше, чем это. Колебания, как вы это описываете, не должны происходить. И если принимающее приложение не может успевать за поступающими данными, размер окна увеличивать не следует.
Отправителю не следует устанавливать время ожидания соединения, не отправив сначала несколько пакетов поддержки активности. Если отправитель тайм-аут соединения без отправки пакетов поддержки активности, то я бы сказал, что в отправителе есть ошибка. Если отправитель отправляет пакеты keep-alive, но получатель не отвечает на них, я бы сказал, что в получателе есть ошибка.
Проверяли ли вы связь с каждого конца соединения, чтобы убедиться в отсутствии значительного сброса пакета, вызвавшего тайм-аут?