socket IO完成端口模型详解

// TestIOCP.cpp : Defines the entry point for the console application.

#include "stdafx.h"

#include <WINSOCK2.h>
#include <stdio.h>

#define PORT 5150
#define MSGSIZE 1024

#pragma comment(lib, "ws2_32.lib")

typedef enum
{
    RECV_POSTED
}OPERATION_TYPE;       //枚举,表示状态

typedef struct
{
    WSAOVERLAPPED   overlap;      
    WSABUF          Buffer;        
    char            szMessage[MSGSIZE];
    DWORD           NumberOfBytesRecvd;
    DWORD           Flags;
    OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;    //定义一个结构体保存IO数据

DWORD WINAPI WorkerThread(LPVOID);

int main(int argc, char* argv[])
{
    WSADATA                  wsaData;
    SOCKET                   sListen, sClient;
    SOCKADDR_IN              local, client;
    DWORD                    i, dwThreadId;
    int                      iaddrSize = sizeof(SOCKADDR_IN);
    HANDLE                   CompletionPort = INVALID_HANDLE_VALUE;
    SYSTEM_INFO              systeminfo;
    LPPER_IO_OPERATION_DATA  lpPerIOData = NULL;

    // Initialize Windows Socket library
    WSAStartup(0x0202, &wsaData);

    // 初始化完成端口
    CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

    // 有几个CPU就创建几个工作者线程
    GetSystemInfo(&systeminfo);
    for (i = 0; i < systeminfo.dwNumberOfProcessors; i++)
    {
        CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
    }

    // 创建套接字
    sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    // 绑定套接字
    local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    local.sin_family = AF_INET;
    local.sin_port = htons(PORT);
    bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

    // 开始监听!
    listen(sListen, 3);

    while (TRUE)//主进程的这个循环中循环等待客户端连接,若有连接,则将该客户套接字于完成端口绑定到一起
      //然后开始异步等待接收客户传来的数据。
    {
        // 如果接到客户请求连接,则继续,否则等待。
        sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
        //client中保存用户信息。
        printf("Accepted client:%s:%d/n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

        //将这个最新到来的客户套接字和完成端口绑定到一起。
        CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)sClient, 0);
        //第三个参数表示传递的参数,这里就传递的客户套接字地址。最后一个参数为0 表示有和CPU一样的进程数。即1个CPU一个线程

        // 初始化结构体
        lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA));
        lpPerIOData->Buffer.len = MSGSIZE; // len=1024
        lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
        lpPerIOData->OperationType = RECV_POSTED; //操作类型
        WSARecv(sClient,         //异步接收消息,立刻返回。
                &lpPerIOData->Buffer, //获得接收的数据
                1,       //The number of WSABUF structures in the lpBuffers array.
                &lpPerIOData->NumberOfBytesRecvd, //接收到的字节数,如果错误返回0
                &lpPerIOData->Flags,       //参数,先不管
                &lpPerIOData->overlap,     //输入这个结构体咯。
                NULL);
    }

    //posts an I/O completion packet to an I/O completion port.
    PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
    CloseHandle(CompletionPort);
    closesocket(sListen);
    WSACleanup();
    return 0;
}

//工作者线程有一个参数,是指向完成端口的句柄
DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
    HANDLE                   CompletionPort=(HANDLE)CompletionPortID;
    DWORD                    dwBytesTransferred;
    SOCKET                   sClient;
    LPPER_IO_OPERATION_DATA  lpPerIOData = NULL;

    while (TRUE)
    {
        GetQueuedCompletionStatus( //遇到可以接收数据则返回,否则等待
                                    CompletionPort,
                                    &dwBytesTransferred, //返回的字数
                                    (DWORD*)&sClient,           //是响应的哪个客户套接字?
                                    (LPOVERLAPPED*)&lpPerIOData, //得到该套接字保存的IO信息
                                    INFINITE);               //无限等待咯。不超时的那种。
        if (dwBytesTransferred == 0xFFFFFFFF)
        {
            return 0;
        }
   
        if (lpPerIOData->OperationType == RECV_POSTED) //如果受到数据
        {
            if (dwBytesTransferred == 0)
            {
                // Connection was closed by client
                closesocket(sClient);
                HeapFree(GetProcessHeap(), 0, lpPerIOData);        //释放结构体
            }
            else
            {
                lpPerIOData->szMessage[dwBytesTransferred] = '/0';
                send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0); //将接收到的消息返回
       
                // Launch another asynchronous operation for sClient
                memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
                lpPerIOData->Buffer.len = MSGSIZE;
                lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
                lpPerIOData->OperationType = RECV_POSTED;
                WSARecv(sClient,               //循环接收
                        &lpPerIOData->Buffer,
                        1,
                        &lpPerIOData->NumberOfBytesRecvd,
                        &lpPerIOData->Flags,
                        &lpPerIOData->overlap,
                        NULL);
            }
        }
    }
    
    return 0;
}

你可能感兴趣的:(socket,IO,struct,null,asynchronous,winapi)