Разъем Windows TCP поддерживает SO_KEEPALIVE по умолчанию?

Я столкнулся с странной ошибкой с сокетами TCP. Кажется, что SO_KEEPALIVE включен по умолчанию во всех сокетах.

Я написал короткий тестовый пример, чтобы создать сокет и подключиться к серверу. Сразу после подключения я проверяю SO_KEEPALIVE с помощью getsockopt . Значение отличное от нуля, которое, согласно MSDN, означает, что поддержка поддерживается. Возможно, я не понимаю этого.

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

Примерно через 2 часа (на самом деле около 1 часа, 59 минут и 19 секунд) для чтения был отправлен пакет завершения, извещающий клиента о том, что соединение больше не открыто. Здесь я начал подозревать SO_KEEPALIVE .

Я пытаюсь понять, почему это произошло. Это вызвало некоторую проблему, поскольку клиенты, которые потеряли соединение по какой-либо причине, должны автоматически подключаться к серверу; в этом случае, поскольку уведомление об отключении не было уведомлено, клиент не подключался повторно до 2 часов.

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

SO_KEEPALIVE не установлен в сокете моим сервером приложений или клиентом.

 // Error checking is removed for this snippet, but all winsock calls succeed. int main() { WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD(2, 2); err = WSAStartup(wVersionRequested, &wsaData); SOCKET foo = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0); DWORD optval; int optlen = sizeof(optval); int test = 0; test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen); std::cout << "Returned " << optval << std::endl; sockaddr_in clientService; clientService.sin_family = AF_INET; clientService.sin_addr.s_addr = inet_addr("127.0.0.1"); clientService.sin_port = htons(446); connect(foo, (SOCKADDR*) &clientService, sizeof(clientService)); test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen); std::cout << "Returned " << optval << std::endl; std::cin.get(); return 0; } // Example output: // Returned 2883584 // Returned 2883584 

Сначала выполните свой тест на чистой установке операционной системы на виртуальной машине. Я подозреваю, что, возможно, что-то еще, что вы установили, пошатнулось настройкой keep alive.

Во-вторых, я сомневаюсь, что поддерживать работоспособность является причиной вашей проблемы. Если keep alive не был включен, у вас никогда не было бы уведомления о закрытии соединения из этого ожидающего чтения. Предполагается, что TCP работает так, что позволяет промежуточным маршрутизаторам уходить и возвращаться, и вы не знаете и не заботитесь. Единственный раз, когда вы будете проинформированы об ошибке, вы пытаетесь отправить сообщение и соединение нарушено (или, в этом случае, если вы попытаетесь отправить и сервер отскочил). Тот факт, что поддерживать работоспособность был включен, означает, что на этом 1hr 59mins отметьте, что TCP-стек передал keep live и заметил, что соединение было отключено. Если сохранить в живых не было, тогда вам пришлось бы ждать, пока вы что-то передали.

Если ваши клиенты должны знать, идет ли соединение вниз, лучше игнорировать его полностью (как вы можете видеть, это влияет на всю машину, даже если вы не тот, кто ее активировал, и для меня это плохое решение ). Если возможно, добавьте пинг и / или тайм-аут уровня приложения в свой протокол. Таким образом, возможно, каждая команда ожидает отклик в течение 30 секунд, и вы отправляете с сервера каждую минуту … Затем вы узнаете о мертвом соединении так быстро, как вам нравится, и вы можете отключиться и снова подключиться в этот момент.

Я использовал это довольно хорошо с моей инфраструктурой сервера ; на самом деле у меня есть стандартный фильтр соединения «asynchronous считывание времени ожидания» и фильтр «повторного установления соединения», который делает тривиальным обеспечение непрерывности соединений. Весь тайм-аут чтения отменяет существующее соединение, и код повторного установления соединения запускается, чтобы воссоздать соединение так же, как если бы соединение было закрыто по какой-либо другой причине.