Короткая версия : я получаю WSA_IO_PENDING при использовании блокирующих вызовов API сокетов.Как мне справиться с этим?Сокет имеет перекрывающийся атрибут ввода-вывода и устанавливается с таймаутом.
Длинная версия :
Платформа : Windows 10. Visual Studio 2015
Гнездо создано очень традиционным простым способом.
s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Сокет имеетПерекрытый по умолчанию ввод / вывод Атрибут включен.Это можно проверить с помощью getsockop / SO_OPENTYPE .
- Мне нужен атрибут с перекрытием, потому что я хочу использовать функцию тайм-аута, например, SO_SNDTIMEO .
- И я бы использовал сокет только в режиме блокировки (т. Е. Синхронно).
- операция чтения сокета выполняется только в пределах одного потока.
- операция записи в сокет можетбыть выполненным из разных потоков, синхронизированных с мьютексом.
Сокет активируется по таймауту и поддерживается с ...
::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, ...);
::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, ...);
::WSAIoctl(s, SIO_KEEPALIVE_VALS, ...);
Операции с сокетом выполняются с
::send(s, sbuffer, ssize, 0);
и
::recv(s, rbuffer, rsize, 0);
Я также пытаюсьиспользовать WSARecv и WSASend с обоими значениями lpOverlapped
и lpCompletionRoutine
, установленными в NULL.
[MSDN] ... Если и lpOverlapped, и lpCompletionRoutineNULL, сокет в этой функции будет рассматриваться как не перекрывающийся сокет.
::WSARecv(s, &dataBuf, 1, &nBytesReceived, &flags, NULL/*lpOverlapped*/, NULL/*lpCompletionRoutine*/)
::WSASend(s, &dataBuf, 1, &nBytesSent, 0, NULL/*lpOverlapped*/, NULL/*lpCompletionRoutine*/)
Проблема :
Эти вызовы блокировки отправки / recv / WSARecv / WSASend вернут ошибку с WSA_IO_PENDING код ошибки!
Вопросы :
Q0: есть ли ссылка на перекрывающийся атрибут с блокирующим вызовом и тайм-аутом?
Как это ведет себя?в случае, если у меня есть сокет с перекрывающимся «атрибутом» + функция тайм-аута, и я просто использую блокирующий API сокета с «не перекрывающейся семантикой ввода-вывода».
Я не смог найти никакой ссылки об этом (например,из MSDN).
Q1: это ожидаемое поведение?
Я наблюдал эту проблему (получить WSA_IO_PENDING) после переноса кода из Win XP / Win 7 в Win 10 .
Вот часть кода клиента: (примечание: утверждение не используется в реальном коде, а просто описывает здесь, что соответствующая ошибка будет обработана и неисправный сокет остановит процедуру..)
auto s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
assert(s != INVALID_SOCKET);
timeval timeout;
timeout.tv_sec = (long)(1500);
timeout.tv_usec = 0;
assert(::setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) != SOCKET_ERROR);
assert(::setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)) != SOCKET_ERROR);
struct tcp_keepalive
{
unsigned long onoff;
unsigned long keepalivetime;
unsigned long keepaliveinterval;
} heartbeat;
heartbeat.onoff = (unsigned long)true;
heartbeat.keepalivetime = (unsigned long)3000;
heartbeat.keepaliveinterval = (unsigned long)3000;
DWORD nob = 0;
assert(0 == ::WSAIoctl(s, SIO_KEEPALIVE_VALS, &heartbeat, sizeof(heartbeat), 0, 0, &nob, 0, 0));
SOCKADDR_IN connection;
connection.sin_family = AF_INET;
connection.sin_port = ::htons(port);
connection.sin_addr.s_addr = ip;
assert(::connect(s, (SOCKADDR*)&connection, sizeof(connection)) != SOCKET_ERROR);
char buffer[100];
int receivedBytes = ::recv(s, buffer, 100, 0);
if (receivedBytes > 0)
{
// process buffer
}
else if (receivedBytes == 0)
{
// peer shutdown
// we will close socket s
}
else if (receivedBytes == SOCKET_ERROR)
{
const int lastError = ::WSAGetLastError();
switch (lastError)
{
case WSA_IO_PENDING:
//.... I get the error!
default:
}
}
Q2: Как мне справиться с этим?
Игнорировать это?или просто закрыть сокет как обычный случай ошибки?
Из наблюдения, как только я получу WSA_IO_PENDING, и если я просто проигнорирую его, сокет в конечном итоге перестанет отвечать на запросы ..
Q3: Как насчет WSAGetOverlappedResult ?
это имеет какой-то смысл?
Какой объект WSAOVERLAPPED я должен дать?Поскольку такого я не использую для всех этих блокирующих вызовов сокетов.
Я попытался просто создать новый пустой WSAOVERLAPPED и использовать его для вызова WSAGetOverlappedResult.Это в конечном счете вернется с успехом с 0 переданным байтом.