После закрытия клиентского сокета на стороне сервера и выхода из приложения, сокет еще некоторое время остается открытым.
Я могу видеть это через netstat
Every 0.1s: netstat -tuplna | grep 6676
tcp 0 0 127.0.0.1:6676 127.0.0.1:36065 TIME_WAIT -
Я использую log4cxx logging и telnet appender. log4cxx использует апрельские сокеты.
Метод Socket :: close () выглядит так:
void Socket::close() {
if (socket != 0) {
apr_status_t status = apr_socket_close(socket);
if (status != APR_SUCCESS) {
throw SocketException(status);
}
socket = 0;
}
}
И это успешно обработано. Но после завершения программы я вижу через netstat открытый сокет, и если он запускается снова, log4cxx не может открыть порт 6676, потому что он занят.
Я пытаюсь изменить log4cxx.
Отключение розетки перед закрытием:
void Socket::close() {
if (socket != 0) {
apr_status_t shutdown_status = apr_socket_shutdown(socket, APR_SHUTDOWN_READWRITE);
printf("Socket::close shutdown_status %d\n", shutdown_status);
if (shutdown_status != APR_SUCCESS) {
printf("Socket::close WTF %d\n", shutdown_status != APR_SUCCESS);
throw SocketException(shutdown_status);
}
apr_status_t close_status = apr_socket_close(socket);
printf("Socket::close close_status %d\n", close_status);
if (close_status != APR_SUCCESS) {
printf("Socket::close WTF %d\n", close_status != APR_SUCCESS);
throw SocketException(close_status);
}
socket = 0;
}
}
Но это не помогло, ошибка все еще воспроизводится.
Это не ошибка. Время ожидания (и ожидание закрытия) разработано в целях безопасности. Однако вы можете настроить время ожидания. В любом случае, с точки зрения сервера, сокет закрыт, и вы расслаблены счетчиком ulimit, он не оказывает заметного влияния, если вы не проводите стресс-тестирование.
Как заметил Кэлвин, это не ошибка, это особенность. Time Wait — это состояние сокета, которое говорит, что этот сокет больше не используется, но, тем не менее, пока не может быть повторно использован.
Представьте, что у вас открыт сокет и какой-то клиент отправляет данные. Данные могут быть скопированы в сети или могут быть в полете, когда сервер закрывает свой сокет.
Теперь представьте, что вы снова запускаете сервис или запускаете новый сервис. Пакеты в сети не знают, что это новая служба, и служба не может знать, что пакеты были предназначены для службы, которая пропала. Новая служба может попытаться проанализировать пакеты и потерпеть неудачу, потому что они имеют какой-то нечетный формат, или клиент может получить несвязанную ошибку и продолжить попытки отправки, возможно, потому что порядковые номера не совпадают, и принимающий хост получит некоторые странная ошибка С учетом времени ожидания клиент получит уведомление о том, что сокет закрыт, и сервер потенциально не сможет получить нечетные данные. Беспроигрышный. Время ожидания должно быть мягким, чтобы все транзитные данные передавались из системы.
Взгляните на этот пост для получения дополнительной информации: Параметры сокетов SO_REUSEADDR и SO_REUSEPORT, чем они отличаются? Означают ли они одно и то же во всех основных операционных системах?
TIME_WAIT
это состояние сокета, чтобы разрешить все в путешествии пакеты, которые могут остаться в соединении, чтобы прибыть или прерваться до того, как параметры соединения (адрес источника, порт источника, адрес назначения, порт назначения) могут быть снова использованы повторно. Ядро просто устанавливает таймер для ожидания истечения этого времени, прежде чем позволить вам снова использовать этот сокет снова. Но вы не можете сократить его (даже если можете, вам лучше этого не делать), потому что у вас нет возможности узнать, есть ли еще пакеты, движущиеся или ускорить или убить их. Единственная возможность, которую вы имеете, — это дождаться, пока сокет, связанный с этим портом, прекратит свое действие и перейдет из состояния TIME_WAIT
к CLOSED
государство.
Если вам было разрешено повторно использовать соединение (я думаю, что есть возможность или что-то можно сделать в ядре Linux), и вы получили старый пакет соединения, вы можете получить сброс соединения из-за полученного пакета. Это может привести к большему количеству проблем в новом соединении. Они решены, заставляя вас ждать, пока весь трафик, принадлежащий старому соединению, не прекратится или не достигнет пункта назначения, прежде чем снова использовать этот сокет.