В сокете Windows TCP по умолчанию включен SO_KEEPALIVE? - PullRequest
2 голосов
/ 07 февраля 2011

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

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

Недавно у меня была странная ошибка, когда сервер отключался дважды подряд.Некоторые клиенты находились в состоянии, когда они отправили информацию для входа и ожидали ответа.Несмотря на то, что в сокете, подключенном к серверу, было размещено перекрывающееся 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

1 Ответ

4 голосов
/ 08 февраля 2011

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

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

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

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

...