Порт завершения ввода-вывода Winsock UDP: невозможно использовать WSASendTo - PullRequest
0 голосов
/ 05 апреля 2019

Я пытаюсь написать сервер UDP с рабочим потоком, который продолжает вызывать GetQueuedCompletionStatus.Я уже могу успешно получать данные с помощью WSARecvFrom, но отправка данных с помощью WSASendTo вызывает следующую ошибку:

10045: The attempted operation is not supported for the type of object referenced.

Я пытался исправить это в течение нескольких часов, читаядокументация, а также строгий метод проб и ошибок.Переключение вызова WSASendTo на блокирующий вызов устраняет проблему, но затем мне приходится иметь дело с блокировкой ввода-вывода.У меня также есть почти такой же код для TCP (WSASend вместо WSASendTo и т. Д.), И он работает с IOCP без помех.

Я пытался вызвать connect, без привязкисокет, изменив структуру OVERLAPPED, проверил, что параметр SOCKADDR* содержит допустимую комбинацию хост / порт, передав параметр lpNumberOfBytesSent.Ничто, похоже, не имеет значения.

Базовый ход программы (код ниже):

  • Создайте сокет с WSA_FLAG_OVERLAPPED
  • Создать порт завершения ввода-вывода и рабочий поток
  • Привязать сокет к 127.0.0.1:2050
  • Вызвать WSARecvFrom (и соединиться с другим клиентом для установки действительного PER_IO_DATA::SockAddrFrom)
  • gets_s введите с консоли и отправьте его с WSASendTo

Моя структура OVERLAPPED:

typedef struct PER_IO_DATA
{
    OVERLAPPED Overlapped;
    SOCKET Socket;
    WSABUF wsaBuf;
    char Buffer[1024];
    DWORD Flags;
    SOCKADDR_IN SockAddrFrom;
} PER_IO_DATA;

Основная функция:

//################################# Initialize ###############################
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    return 0;

//Create completion port
HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (!hCompletionPort)
    return 0;

//Create and detach a worker thread
HANDLE hThread = CreateThread(NULL, 0, server_worker_thread, hCompletionPort, 0, NULL);
CloseHandle(hThread);

//Create UDP socket with overlapped IO
SOCKET Sock = WSASocketW(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Sock == INVALID_SOCKET)
    WSAERROR();

//################################### Bind ###################################
//Bind socket to 127.0.0.1:2050
SOCKADDR_IN serverAddr = { 0 };
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(2050);
InetPtonA(AF_INET, "127.0.0.1", &serverAddr.sin_addr.s_addr);
if (bind(Sock, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR_IN)) == -1)
    WSAERROR();

//Assign completion port
CreateIoCompletionPort((HANDLE)Sock, hCompletionPort, 0, 0);

//Create overlapped context
PER_IO_DATA* pPerIoData = malloc(sizeof(PER_IO_DATA));
ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));

pPerIoData->Socket = Sock;
pPerIoData->Overlapped.hEvent = WSACreateEvent();
pPerIoData->wsaBuf.buf = pPerIoData->Buffer;
pPerIoData->wsaBuf.len = sizeof(pPerIoData->Buffer);

//################################## Receive #################################
int size = sizeof(SOCKADDR_IN);
if (WSARecvFrom(Sock, &pPerIoData->wsaBuf, 1, NULL,
    &pPerIoData->Flags, (SOCKADDR*)&pPerIoData->SockAddrFrom,
    &size, &pPerIoData->Overlapped, NULL) == SOCKET_ERROR)
{
    if (WSAGetLastError() != WSA_IO_PENDING)
        WSAERROR();
}

//################################### Send ###################################
while (TRUE)
{
    gets_s(pPerIoData->Buffer, sizeof(pPerIoData->Buffer));
    pPerIoData->wsaBuf.len = strlen(pPerIoData->Buffer);

    //Print outgoing data
    char addr[16];
    DEBUG("Sending %.*s (%d bytes) to %s:%d", pPerIoData->wsaBuf.len, pPerIoData->Buffer,
        pPerIoData->wsaBuf.len, InetNtopA(AF_INET, &pPerIoData->SockAddrFrom.sin_addr,
        addr, sizeof(addr)), ntohs(pPerIoData->SockAddrFrom.sin_port));

    //Send the data (this is where the error happens)
    if (WSASendTo(Sock, &pPerIoData->wsaBuf, 1, NULL,
        WSA_FLAG_OVERLAPPED, (SOCKADDR*)&pPerIoData->SockAddrFrom, sizeof(SOCKADDR_IN),
        &pPerIoData->Overlapped, NULL) == SOCKET_ERROR)
    {
        if (WSAGetLastError() != WSA_IO_PENDING)
            WSAERROR(); //<-- Error 10045
    }
}

//################################## Clean up ################################
shutdown(Sock, SD_BOTH);
closesocket(Sock);

WSACleanup();
return 0;

Пример вывода:

Received hello (5 bytes) from 127.0.0.1:50995
respond
Sending respond (7 bytes) to 127.0.0.1:50995
Error 10045: The attempted operation is not supported for the type of object referenced.

Сообщение об ошибке исходит из моего WSAERROR макроса.

Как мне позвонить WSASendTo с WSA_FLAG_OVERLAPPED и добавить его в очередь сообщений IOCP?

...