Я хочу избежать состояния TIME_WAIT при закрытии сокета TCP (мне известны плюсы и минусы обхода TIME_WAIT).
Я использую Windows и WinSock2 / .Netя испытываю большие затруднения при работе опции сокета SO_LINGER
, как описано в документации .
Мой тестовый код с большей частью удаленной проверки ошибок для краткости:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
int main()
{
std::cout << "starting..." << std::endl;
WSADATA w = { 0 };
int error = WSAStartup(0x0202, &w);
if (error || w.wVersion != 0x0202) {
std::cerr << "Could not initialise Winsock2." << std::endl;
return -1;
}
auto clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Set socket options
linger lingerOpt = { 1, 0 };
setsockopt(clientSocket, SOL_SOCKET, SO_LINGER, (char*)&lingerOpt, sizeof(lingerOpt));
linger checkLingerOpt{ 0 };
int optLen = sizeof(checkLingerOpt);
int getOptResult = getsockopt(clientSocket, SOL_SOCKET, SO_LINGER, (char*)&checkLingerOpt, &optLen);
if (getOptResult < 0) {
wprintf(L"Failed to get SO_LINGER socket option on client socket, error: %ld\n", WSAGetLastError());
}
else {
std::cout << "Linger option set to onoff " << checkLingerOpt.l_onoff << ", linger seconds " << checkLingerOpt.l_linger << "." << std::endl;
}
// Bind local client socket.
sockaddr_in clientBindAddr;
clientBindAddr.sin_family = AF_INET;
clientBindAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
clientBindAddr.sin_port = htons(15064);
bind(clientSocket, (SOCKADDR*)&clientBindAddr, sizeof (clientBindAddr));
sockaddr_in serverSockAddr;
serverSockAddr.sin_family = AF_INET;
serverSockAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serverSockAddr.sin_port = htons(5060);
// Connect to server.
connect(clientSocket, (SOCKADDR*)&serverSockAddr, sizeof (serverSockAddr));
std::cout << "connected." << std::endl;
Sleep(1000);
//shutdown(clientSocket, SD_BOTH);
closesocket(clientSocket);
std::cout << "finished." << std::endl;
}
Результат:
starting...
Linger option set to onoff 1, linger seconds 0.
connected.
finished.
Приведенный выше пример избегает состояния TIME_WAIT, но делает это потому, что клиентский сокет отправляет пакет RST.
Если параметр Linger
изменен на:
linger lingerOpt = { 1, 5 };
Результат
starting...
Linger option set to onoff 1, linger seconds 5.
connected.
finished.
Тогда закрытие сокета приведет к TIME_WAIT, но к 30 с, чтотот же результат, что и не установка опции SO_LINGER
.
Другойнаблюдение состоит в том, что если сокет отключается (что является рекомендуемым способом чистого закрытия) с помощью shutdown(clientSocket, SD_BOTH);
, то Lingerопция {1,0}
не окажет влияния.
В итоге:
- Установите опцию Linger как {1,0} и закройте с помощью closesocket => RST.
- Установите опцию Linger как {1,5} и закройте с помощью closesocket => FIN-ACK & TIME_WAIT 30 с.
- Установите опцию Linger как {1,0} и закройте с выключением, closesocket => FIN-ACK& TIME_WAIT - 30 с.
- Установить опцию задержки в {1,5} и закрыть с выключением, closesocket => FIN-ACK & TIME_WAIT - 30 с.
Что бы я хотел:
Установить опцию Linger как {1,0} и закрыть при выключении, closesocket => FIN-ACK & TIME_WAIT, равном 0 с.
Обновление: Как указано в ссылке closesocket от Remy Lebeau
параметр Linger {ненулевой, 0} жестко задан для генерации RST.
Короткое значение TIME_WAIT в несколько секунд будет таким же хорошим, то есть длительная опция {1,1} заставит closesocket
изящно завершиться с периодом TIME_WAIT в 1 с, что в соответствии с closesocket
документация должна быть возможной.
Обновление 2: Как еще раз отмечено Remy Lebeau
, опция Linger и период TIME_WAIT НЕ связаны. Если вы читаете это, вы, вероятно, совершили ту же ошибку, что и я, и пытались сократить период TIME_WAIT с помощью setsockopt
и SO_LINGER
.
Для всех учетных записей, которые не могут быть выполнены, и в случаях, когда необходимо тщательно оценить судей, нужно избегать TIME_WAIT (например, в моем случае, когда протокол прикладного уровня может работать с паразитными или потерянными пакетами данных TCP), идеальныйопция выглядит как установка Linger {1,0} для принудительного закрытия жесткого сокета RST
, что позволит немедленно повторить попытку подключения без ОС, блокирующей попытку.