Сервер TCP Socket Периодически создает CLOSE_WAITs С течением времени, пока не выйдет из строя

StackOverflow https://stackoverflow.com/questions/679643

Вопрос

Надеюсь, кто-нибудь сможет нам помочь, поскольку мы продвигаемся настолько далеко, насколько это возможно в расследовании!

У нас есть простой асинхронный сервер сокетов, написанный на C #, который принимает подключения от ASP.NET веб-приложения, отправляет сообщение, выполняет некоторую обработку (обычно в базе данных, но также и в других системах), а затем отправляет ответ обратно клиенту.Клиент отвечает за закрытие соединения.

У нас возникали проблемы, из-за которых, если система находится под большой нагрузкой в течение длительного периода времени (обычно дней), сокеты CLOSE_WAIT накапливаются на сервере (netstat -a) до такой степени, что процесс не будет принимать никаких дальнейших подключений.В этот момент мы должны прервать процесс, и он снова запустится.

Мы попытались запустить несколько нагрузочных тестов нашего приложения ASP.NET чтобы попытаться воспроизвести проблему (поскольку вывести какую-то проблему из кода было невозможно).Мы думаем, что нам это удалось, и в итоге мы получили WireShark трассировка пакетов о проблеме, проявляющейся как исключение SocketException в журналах сервера сокетов:

System.Net.Sockets.Исключение SocketException:Существующее соединение было принудительно закрыто удаленным хостом в System.Net.Sockets.Сокет.BeginSend(буфер Байт[], смещение Int32, размер Int32, SocketFlags socketFlags, обратный вызов AsyncCallback, состояние объекта)

Я попытался воспроизвести проблему из трассировки пакетов как однопоточный процесс, напрямую взаимодействующий с сервером сокетов (используя тот же код, что и приложение ASP.NET), но не смог.

Есть ли у кого-нибудь какие-либо предложения о том, что еще можно попробовать, проверить или очевидные вещи, которые мы, возможно, делаем неправильно?

Это было полезно?

Решение

Посмотрите на диаграмму

http://en.wikipedia.org/wiki/File:Tcp_state_diagram_fixed.svg

Ваш клиент закрыл соединение, вызвав close() , который отправил FIN на серверный сокет, который подтвердил FIN и состояние которого теперь изменилось на CLOSE_WAIT , и остается таким, если сервер не выполнит вызов close() для этого сокета.

Ваша серверная программа должна определить, прервал ли клиент соединение, а затем немедленно закрыть () его, чтобы освободить порт.Каким образом?Обратитесь к read().При чтении конца файла (что означает получение FIN) возвращается ноль.

Другие советы

Если ваш сервер накапливает CLOSE_WAIT сокеты, то он не закрывает свой сокет после завершения подключения.Если вы взглянете на диаграмму состояний в комментарии к сообщению Криса, вы увидите, что CLOSE_WAIT переходы к LAST_ACK как только розетка будет закрыта и FIN был отправлен.

Вы говорите, что сложно определить, где это сделать, из-за асинхронного характера?Это не должно быть проблемой, вы должны закрыть сокет, если обратный вызов из вашего recv возвращает 0 байт (при условии, что вам больше нечего делать, как только ваш клиент закроет свою сторону соединения).Если вам действительно нужно беспокоиться о продолжении отправки, то выполните завершение работы (recv) здесь и отметьте, что ваш клиент закрыт, как только вы закончите отправку, выполните завершение работы (send) и закрытие.

ВОЗМОЖНО, вы выдаете новое чтение при обратном вызове из read, которое возвращает 0, указывающее, что клиент закрыт, и это может вызывать у вас проблемы?

Клиент отвечает за закрытие соединения.

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

using (Socket s = new Socket(/* */)) {
  /* Do stuff */
  s.Shutdown(SocketShutdown.Both);
  s.Close();
}

Вы не должны перекладывать ответственность за закрытие TCP-сокетов только на клиента.Что произойдет, если клиентский процесс / компьютер выйдет из строя?

В идеале у вас должен быть установлен тайм-аут, чтобы, если по истечении определенного промежутка времени на подключенный сокет не поступает трафик, сервер закрывал его.

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

Эта выдача команды закрытия просто сообщает слушателю (серверу), что соединение необходимо завершить.

Проще говоря, когда сервер снова выдает команду чтения (listener.read() или listener.beginread(...) в асинхронном режиме), чтение вернет прочитанное значение в 0 байт, это само по себе указывает на то, что сокет должен быть закрыт слушателем, поскольку любые другие операции с сокетом были прекращены клиентом.

CLOSE_WAIT предназначены для зависания на некоторое время после закрытия сокета, чтобы предотвратить повторное использование того же номера сокета и получение пакетов от старого соединения.Это доставит вам неприятности только в том случае, если вы действительно быстро открываете и закрываете огромное количество сокетов.

РЕДАКТИРОВАТЬ - Это должно быть TIME_WAIT, а не CLOSE_WAIT выше.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top