Winsock2 - How to use IOCP on client side

孤街浪徒 提交于 2019-11-30 07:52:51
Remy Lebeau

Try something like this:

Client:

#include <iostream>
#include <string>
#include <winsock2.h> 
#pragma comment(lib, "ws2_32.lib") 

typedef struct 
{ 
    WSAOVERLAPPED Overlapped; 
    SOCKET Socket; 
    WSABUF wsaBuf; 
    char Buffer[1024];
    DWORD Flags;
} PER_IO_DATA, * LPPER_IO_DATA; 

static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter) 
{ 
    HANDLE hCompletionPort = (HANDLE)lpParameter; 
    DWORD NumBytesRecv = 0; 
    ULONG CompletionKey; 
    LPPER_IO_DATA PerIoData; 

    while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesRecv, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))
    {
        if (!PerIoData)
            continue;

        if (NumBytesRecv == 0) 
        {
            std::cout << "Server disconnected!\r\n\r\n";  
        }
        else
        {
            // use PerIoData->Buffer as needed...
            std::cout << std::string(PerIoData->Buffer, NumBytesRecv);

            PerIoData->wsaBuf.len = sizeof(PerIoData->Buffer); 
            PerIoData->Flags = 0; 

            if (WSARecv(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesRecv, &(PerIoData->Flags), &(PerIoData->Overlapped), NULL) == 0)
                continue;

            if (WSAGetLastError() == WSA_IO_PENDING)
                continue;
        }

        closesocket(PerIoData->Socket);
        delete PerIoData;
    } 

    return 0; 
} 

int main(void) 
{ 
    WSADATA WsaDat; 
    if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
        return 0; 

    HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 
    if (!hCompletionPort)
        return 0;

    SYSTEM_INFO systemInfo; 
    GetSystemInfo(&systemInfo); 

    for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i) 
    { 
        HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL); 
        CloseHandle(hThread); 
    } 

    SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 
    if (Socket == INVALID_SOCKET)
        return 0;

    SOCKADDR_IN SockAddr; 
    SockAddr.sin_family = AF_INET; 
    SockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    SockAddr.sin_port = htons(8888); 

    CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, 0, 0); 

    if (WSAConnect(Socket, (SOCKADDR*)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR)
        return 0;

    PER_IO_DATA *pPerIoData = new PER_IO_DATA;
    ZeroMemory(pPerIoData, sizeof(PER_IO_DATA)); 

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

    DWORD dwNumRecv;
    if (WSARecv(Socket, &(pPerIoData->wsaBuf), 1, &dwNumRecv, &(pPerIoData->Flags), &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
    {
        if (WSAGetLastError() != WSA_IO_PENDING)
        {
            delete pPerIoData;
            return 0;
        }
    } 

    while (TRUE) 
        Sleep(1000); 

    shutdown(Socket, SD_BOTH); 
    closesocket(Socket); 

    WSACleanup(); 
    return 0; 
} 

Server:

#include <iostream>  
#include <winsock2.h>  
#pragma comment(lib,"ws2_32.lib")  

typedef struct
{
    WSAOVERLAPPED Overlapped;
    SOCKET Socket;
    WSABUF wsaBuf;
    char Buffer[1024];
    DWORD BytesSent;
    DWORD BytesToSend;
} PER_IO_DATA, * LPPER_IO_DATA; 


static DWORD WINAPI ServerWorkerThread(LPVOID lpParameter)
{
    HANDLE hCompletionPort = (HANDLE)lpParameter;
    DWORD NumBytesSent = 0;
    ULONG CompletionKey;
    LPPER_IO_DATA PerIoData;

    while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesSent, &CompletionKey, (LPOVERLAPPED*)&PerIoData, INFINITE))    
    {
        if (!PerIoData)
            continue;

        if (NumBytesSent == 0)
        {
            std::cout << "Client disconnected!\r\n\r\n";
        }
        else
        {
            PerIoData->BytesSent += NumBytesSent;
            if (PerIoData->BytesSent < PerIoData->BytesToSend)
            {
                PerIoData->wsaBuf.buf = &(PerIoData->Buffer[PerIoData->BytesSent]);
                PerIoData->wsaBuf.len = (PerIoData->BytesToSend - PerIoData->BytesSent);
            }
            else
            {
                PerIoData->wsaBuf.buf = PerIoData->Buffer;
                PerIoData->wsaBuf.len = strlen(PerIoData->Buffer);
                PerIoData->BytesSent = 0;
                PerIoData->BytesToSend = PerIoData->wsaBuf.len;
            }

            if (WSASend(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesSent, 0, &(PerIoData->Overlapped), NULL) == 0)
                continue;

            if (WSAGetLastError() == WSA_IO_PENDING)
                continue;
        }

        closesocket(PerIoData->Socket);
        delete PerIoData;
    }

    return 0;
} 

int main()  
{  
    WSADATA WsaDat;  
    if (WSAStartup(MAKEWORD(2,2), &WsaDat) != 0)
        return 0;  

    HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
    if (!hCompletionPort)
        return 0;

    SYSTEM_INFO systemInfo;
    GetSystemInfo(&systemInfo);

    for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i)
    {
        HANDLE hThread = CreateThread(NULL, 0, ServerWorkerThread, hCompletionPort, 0, NULL);
        CloseHandle(hThread);
    } 

    SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);  
    if (listenSocket == INVALID_SOCKET)
        return 0;  

    SOCKADDR_IN server;
    ZeroMemory(&server, sizeof(server));
    server.sin_family = AF_INET;  
    server.sin_addr.s_addr = INADDR_ANY;  
    server.sin_port = htons(8888);  

    if (bind(listenSocket, (SOCKADDR*)(&server), sizeof(server)) != 0)
        return 0;  

    if (listen(listenSocket, 1) != 0)
        return 0;  

    std::cout << "Waiting for incoming connection...\r\n";  

    SOCKET acceptSocket;  
    do  
    {  
        sockaddr_in saClient;  
        int nClientSize = sizeof(saClient);  
        acceptSocket = WSAAccept(listenSocket, (SOCKADDR*)&saClient, &nClientSize, NULL, NULL);  
    }
    while (acceptSocket == INVALID_SOCKET);

    std::cout << "Client connected!\r\n\r\n";  

    CreateIoCompletionPort((HANDLE)acceptSocket, hCompletionPort, 0, 0); 

    LPPER_IO_DATA pPerIoData = new PER_IO_DATA;
    ZeroMemory(pPerIoData, sizeof(PER_IO_DATA));

    strcpy(pPerIoData->Buffer, "Welcome to the server!\r\n");  

    pPerIoData->Overlapped.hEvent = WSACreateEvent(); 
    pPerIoData->Socket = acceptSocket; 
    pPerIoData->wsaBuf.buf = pPerIoData->Buffer;  
    pPerIoData->wsaBuf.len = strlen(pPerIoData->Buffer);  
    pPerIoData->BytesToSend = pPerIoData->wsaBuf.len;  

    DWORD dwNumSent;
    if (WSASend(acceptSocket, &(pPerIoData->wsaBuf), 1, &dwNumSent, 0, &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR)
    {
        if (WSAGetLastError() != WSA_IO_PENDING)
        {
            delete pPerIoData;
            return 0;
        }
    }  

    while (TRUE)  
        Sleep(1000);  

    shutdown(acceptSocket, SD_BOTH);  
    closesocket(acceptSocket);  

    WSACleanup();  
    return 0;  
}

Have you had a look at the example in the MSDN documentation for WSARecv?

Essentially, you have to start the asynchronous WSARecv operation first and then get notified via the completion port of its completion.

Or to put it another way: Windows I/O completion ports are using a proactor model (in contrast to the reactor model of Linux/FreeBSD/NetBSD).

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!