Те, кто работает с TCP-сокетами, знают что сокет может работать в блокирующем или неблокирующем (nonblocking) режиме. Windows-сокеты, после создания, находятся в блокирующем режиме, но их можно перевести в неблокирующий функцией ioctlsocket().
При работе над одним проектом у меня появилась задача определить, в каком режиме работает сокет, предоставленный мне DLL-функцией. Т.е. кроме самого сокета, у меня никакой информации не было и приходили они в непредсказуемом режиме.
Под *nix blocking-режим без проблем определяется вызовом функции fcntl(), но под WinSock2 ничего подобного не обнаружилось, и на форумах ничего кроме «Windows does not offer any way to query whether a socket is currently set to blocking or non-blocking» никто не ответил.
Но способ определения все-таки существует:
Функция принимает номер сокета и возвращает 1 если сокет в nonblocking mode, 0 — blocking, -1 если произошла ошибка определения и -2 если сокет после определения режима остался с маленьким таймаутом.
Вкратце последовательность действий такова:
1. Сохраняем значение таймаута сокета (по умолчанию там 0 — «ждать вечно»).
2. Устанавливаем таймаут в 1 милисекунду.
3. Читаем из сокета 0 (ноль) байт Out of Band данных. Тут нужно пояснение: Если передаются OOB-данные, то функция может врать, но я никогда не сталкивался с OOB с тех пор, как Windows NT4 валилась в синий экран при попытке принять такое (WinNuke).
4. Получаем ошибку, которая произошла в результате чтения.
5. Восстанавливаем старое значение таймаута сокета.
6. Смотрим что за ошибка чтения у нам была: если WSAEWOULDBLOCK — то сокет находится в nonblocking mode, что и требовалось определить.
К недостаткам данного метода, кроме уже упомянутого MSG_OOB, можно отнести задержку определения в 1 миллисекунду и ненулевую вероятность испортить таймаут чтения сокета (хотя нагрузочный тест ни разу такого поведения не выявил).
При работе над одним проектом у меня появилась задача определить, в каком режиме работает сокет, предоставленный мне DLL-функцией. Т.е. кроме самого сокета, у меня никакой информации не было и приходили они в непредсказуемом режиме.
Под *nix blocking-режим без проблем определяется вызовом функции fcntl(), но под WinSock2 ничего подобного не обнаружилось, и на форумах ничего кроме «Windows does not offer any way to query whether a socket is currently set to blocking or non-blocking» никто не ответил.
Но способ определения все-таки существует:
// GetBlockingMode возвращает: 1 - nonblocking | 0 - blocking | -1 - error | -2 - timeout reseted!
int GetBlockingMode(int Sock)
{
int iSize, iValOld, iValNew, retgso;
iSize = sizeof(iValOld);
retgso = getsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValOld, &iSize); // Save current timeout value
if (retgso == SOCKET_ERROR) return (-1);
iValNew = 1;
retgso = setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValNew, iSize); // Set new timeout to 1 ms
if (retgso == SOCKET_ERROR) return (-1);
// Ok! Try read 0 bytes.
char buf[1]; // 1 - why not :)
int retrcv = recv(Sock, buf, 0, MSG_OOB); // try read MSG_OOB
int werr = WSAGetLastError();
retgso = setsockopt(Sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iValOld, iSize); // Set timeout to initial value
if (retgso == SOCKET_ERROR) return (-2);
if (werr == WSAENOTCONN) return (-1);
if (werr == WSAEWOULDBLOCK) return 1;
return 0;
}
Функция принимает номер сокета и возвращает 1 если сокет в nonblocking mode, 0 — blocking, -1 если произошла ошибка определения и -2 если сокет после определения режима остался с маленьким таймаутом.
Вкратце последовательность действий такова:
1. Сохраняем значение таймаута сокета (по умолчанию там 0 — «ждать вечно»).
2. Устанавливаем таймаут в 1 милисекунду.
3. Читаем из сокета 0 (ноль) байт Out of Band данных. Тут нужно пояснение: Если передаются OOB-данные, то функция может врать, но я никогда не сталкивался с OOB с тех пор, как Windows NT4 валилась в синий экран при попытке принять такое (WinNuke).
4. Получаем ошибку, которая произошла в результате чтения.
5. Восстанавливаем старое значение таймаута сокета.
6. Смотрим что за ошибка чтения у нам была: если WSAEWOULDBLOCK — то сокет находится в nonblocking mode, что и требовалось определить.
К недостаткам данного метода, кроме уже упомянутого MSG_OOB, можно отнести задержку определения в 1 миллисекунду и ненулевую вероятность испортить таймаут чтения сокета (хотя нагрузочный тест ни разу такого поведения не выявил).