TCP Socket Server иногда создает CLOSE_WAIT с течением времени, пока он не работает - PullRequest
4 голосов
/ 25 марта 2009

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

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

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

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

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

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

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

Ответы [ 6 ]

5 голосов
/ 26 марта 2009

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

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

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

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

5 голосов
/ 25 марта 2009

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

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

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

3 голосов
/ 25 марта 2009

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

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

using (Socket s = new Socket(/* */)) {
  /* Do stuff */
  s.Shutdown(SocketShutdown.Both);
  s.Close();
}
0 голосов
/ 19 апреля 2012

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

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

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

0 голосов
/ 25 марта 2009

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

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

0 голосов
/ 25 марта 2009

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

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

...