Разъем открыт после процесса, который открыл его закончил

После закрытия клиентского сокета на стороне сервера и выхода из приложения, сокет еще некоторое время остается открытым.

Я могу видеть это через 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;
    }
}

Но это не помогло, ошибка все еще воспроизводится.

3 ответа

Как заметил Кэлвин, это не ошибка, это особенность. Time Wait - это состояние сокета, которое говорит, что этот сокет больше не используется, но, тем не менее, пока не может быть повторно использован.

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

Теперь представьте, что вы снова запускаете сервис или запускаете новый сервис. Пакеты в сети не знают, что это новая служба, и служба не может знать, что пакеты были предназначены для службы, которая пропала. Новая служба может попытаться проанализировать пакеты и потерпеть неудачу, потому что они имеют какой-то нечетный формат, или клиент может получить несвязанную ошибку и продолжить попытки отправки, возможно, потому что порядковые номера не совпадают, и принимающий хост получит некоторые странная ошибка С учетом времени ожидания клиент получит уведомление о том, что сокет закрыт, и сервер потенциально не сможет получить нечетные данные. Беспроигрышный. Время ожидания должно быть мягким, чтобы все транзитные данные передавались из системы.

Взгляните на этот пост для получения дополнительной информации: опции сокетов SO_REUSEADDR и SO_REUSEPORT, чем они отличаются? Означают ли они одно и то же во всех основных операционных системах?

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

TIME_WAIT это состояние сокета, позволяющее всем находящимся в поездке пакетам, которые могут остаться в соединении, прибыть или прерваться до того, как параметры соединения (адрес источника, порт источника, адрес назначения, порт назначения) могут быть снова использованы повторно. Ядро просто устанавливает таймер, чтобы дождаться истечения этого времени, прежде чем позволить вам снова использовать этот сокет. Но вы не можете сократить его (даже если можете, вам лучше этого не делать), потому что у вас нет возможности узнать, есть ли еще пакеты, движущиеся или ускорить или убить их. Единственная возможность, которую вы имеете, - это дождаться, пока сокет, связанный с этим портом, прекратит свое действие и перейдет из состояния TIME_WAIT к CLOSED государство.

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

Другие вопросы по тегам