网络编程(56)—— Windows下使用IOCP构建socket服务端

一、完成端口对象

        不同于IO重叠对象在IO完成时通过触发事件或者触发CompletionRoutine回调函数,IOCP模型将socket和完成端口对象(CompletionPort,简称CP对象)绑定,当IO操作完成时,会改变该对象的状态,而我们通过完成端口对象,便可以确认IO操作是否完成。

       创建完成端口对象的API如下:

HANDLE WINAPI CreateIoCompletionPort(
  __in     HANDLE FileHandle,
  __in_opt HANDLE ExistingCompletionPort,
  __in     ULONG_PTR CompletionKey,
  __in     DWORD NumberOfConcurrentThreads
);

CreateIoCompletionPort既可以创建完成端口对象,也可以用来将socket和完成对象绑定,通过对其赋予不同的参数,可以实现不同的功能:

FileHandle ——创建CP对象时传入INVALID_HANDLE_VALUE;绑定socket和CP对象时传入socket描述符。

ExistingCompletionPort——创建CP对象时传入NULL;绑定socket和CP对象传入完成端口对象的句柄;

CompletionKey —— 创建CP对象是传入0;绑定socket和CP对象时作为参数传递给GetQueuedCompletionStatus

NumberOfConcurrentThreads—— 分配给CP对象用于处理IO的线程数。如果参数是0,系统中的CPU个数就是最大的线程数。

返回值 —— 返回CP对象的句柄。

        如下,我们创建了一个完成端口对象:

hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

二、进行socket对象和CP对象的绑定

        我们仍然用CreateIoCompletionPort将socket和CP对象进行绑定:

CreateIoCompletionPort((HANDLE)clntSock,hComPort,(DWORD)handleInfo,0);

        clntSock是我们要进行绑定的socket描述符,hComPort使我们之前创建的CP对象,而handleInfo是我们自定义的结构体,作为参数可以传给GetQueuedCompletionStatus函数。

三、使用GetQueuedCompletionStatus获取CP对象的状态

        GetQueuedCompletionStatus在我们定义的线程函数中调用,用于获取CP对象的状态。当IO操作未完成时,该函数会发生阻塞;若IO操作完成,函数发生返回。其函数原型如下:

BOOL WINAPI GetQueuedCompletionStatus(
  __in  HANDLE CompletionPort,
  __out LPDWORD lpNumberOfBytes,
  __out PULONG_PTR lpCompletionKey,
  __out LPOVERLAPPED *lpOverlapped,
  __in  DWORD dwMilliseconds
);

CompletionPort —— 进行注册过的完成对象的句柄。

lpNumberOfBytes —— 完成IO对象时传递或者接受的字节数。

lpCompletionKey —— 使用CreateIoCompletionPort注册时传递的参数,参数可以传递我们自定义的结构信息。

lpOverlapped —— 调用WSARecv或者WSASend时传递的OVERLAPPED对象指针。

dwMilliseconds —— GetQueuedCompletionStatus阻塞的时间,如果设置成INFINITE时无限期等待。

四、代码示例

#include "stdafx.h"
#include "stdio.h"
#include "process.h"
#include "stdlib.h"
#include "WinSock2.h"
#include "Windows.h"
 
#pragma comment(lib,"ws2_32.lib")
 
#define BUF_SIZE 100
#define READ 3
#define WRITE 5
 
typedef struct 
{
    SOCKEThClntSock;
    SOCKADDRclntAdr;
}HANDLE_DATA,*LPHANDLE_DATA;
 
typedef struct
{
    OVERLAPPEDoverlapped;
    WSABUFwsaBuf;
    char buffer[BUF_SIZE];
    int rwMode;
}IO_DATA,*LPIO_DATA;
 
unsigned WINAPI ThreadMain(LPVOID CompletionPortIO);
void ErrorHandler(char* message);
 
int _tmain(int argc, _TCHAR* argv[])
{
    WSADatawsaData;
    HANDLEhComPort;
    SYSTEM_INFOsysInfo;
    LPIO_DATAioInfo;
    LPHANDLE_DATAhandleInfo;
 
    SOCKETservSock;
    SOCKADDR_INservAddr;
    DWORDrecvBytes,i,flags=0;
    WSAStartup(MAKEWORD(2,2),&wsaData);
   
    hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
    GetSystemInfo(&sysInfo);
 
    for(i=0;i<sysInfo.dwNumberOfProcessors;i++)
        _beginthreadex(NULL,0,ThreadMain,(LPVOID)hComPort,0,NULL);
 
    servSock=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
    if(servSock==INVALID_SOCKET)
        ErrorHandler("WSASocket Error");
 
    memset(&servAddr,0,sizeof(servAddr));
    servAddr.sin_family=AF_INET;
    servAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servAddr.sin_port=htons(atoi("8888"));
 
    if(bind(servSock,(SOCKADDR*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
        ErrorHandler("bind error");
 
    if(listen(servSock,5)==SOCKET_ERROR)
        ErrorHandler("listen error");
 
    while(1)
    {
        SOCKETclntSock;
        SOCKADDR_INclntAddr;
        int clntAddrSz;
 
        clntSock=accept(servSock,(SOCKADDR*)&clntAddr,&clntAddrSz);
        handleInfo=(LPHANDLE_DATA)malloc(sizeof(HANDLE_DATA));
        handleInfo->hClntSock=clntSock;
        memcpy(&(handleInfo->clntAdr),&clntAddr,sizeof(clntAddr));
 
        CreateIoCompletionPort((HANDLE)clntSock,hComPort,(DWORD)handleInfo,0);
 
        ioInfo=(LPIO_DATA)malloc(sizeof(IO_DATA));
        memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
        ioInfo->wsaBuf.len=BUF_SIZE;
        ioInfo->wsaBuf.buf=ioInfo->buffer;
        ioInfo->rwMode=READ;
        WSARecv(handleInfo->hClntSock,&(ioInfo->wsaBuf),1,&recvBytes,&flags,&(ioInfo->overlapped),NULL);
    }
   
    WSACleanup();
    return 0;
}
 
unsigned WINAPI ThreadMain(LPVOID CompletionPortIO)
{
    HANDLEhComPort=(HANDLE)CompletionPortIO;
    SOCKET sock;
    DWORDbytesTrans;
    LPHANDLE_DATAhandleInfo;
    LPIO_DATAioInfo;
    DWORD flags;
 
    while(1)
    {
    GetQueuedCompletionStatus(hComPort,&bytesTrans,(PULONG_PTR)&handleInfo,(LPOVERLAPPED*)&ioInfo,INFINITE);
        sock=handleInfo->hClntSock;
 
        if(ioInfo->rwMode==READ)
        {
            puts("message received!");
            if(bytesTrans==0)
            {
                closesocket(sock);
                free(handleInfo);
                free(ioInfo);
                continue;
            }
            memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
            ioInfo->wsaBuf.len=bytesTrans;
            ioInfo->rwMode=WRITE;
            WSASend(sock,&(ioInfo->wsaBuf),1,NULL,0,&(ioInfo->overlapped),NULL);
 
            ioInfo=(LPIO_DATA)malloc(sizeof(IO_DATA));
            memset(&(ioInfo->overlapped),0,sizeof(OVERLAPPED));
            ioInfo->wsaBuf.len=BUF_SIZE;
            ioInfo->wsaBuf.buf=ioInfo->buffer;
            ioInfo->rwMode=READ;
            WSARecv(sock,&(ioInfo->wsaBuf),1,NULL,&flags,&(ioInfo->overlapped),NULL);
        }
        else
        {
            puts("message sent");
            free(ioInfo);
        }
    }
    return 0;
}
 
void ErrorHandler(char* message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}


 Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL56


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用IOCP完成端口和Socket封装的异步TCP类是一种成熟的技术,它能够有效地处理大量并发请求,并且能够提高系统的性能和效率。首先,IOCP能够充分利用系统资源,通过将I/O操作交给内核来完成,可以实现高效的异步操作。而端口和Socket封装则可以简化开发者的编程工作,提供更加友好的接口和操作方法,从而提高开发效率和代码的可读性。 此外,使用IOCP完成端口和Socket封装的异步TCP类还具有良好的扩展性和灵活性。通过合理设计类的结构和接口,可以实现类的继承和重载,满足不同应用场景和需求。同时,IOCP完成端口也可以与其他技术和框架结合,如线程池、事件驱动等,从而实现更加强大和复杂的系统功能。 然而,使用IOCP完成端口和Socket封装的异步TCP类也存在一些局限性。比如对于初学者来说,可能需要一定的学习成本,需要掌握一定的操作和调试技巧。同时,如果应用不当,可能会导致系统资源的浪费和性能下降。因此,在使用这种技术时,需要开发者具有一定的经验和技术水平,可以充分发挥其优势,避免其劣势的影响。 总的来说,使用IOCP完成端口和Socket封装的异步TCP类是一种成熟的技术,它具有高效、灵活、可扩展等优势,能够满足大规模并发请求的处理需求,为系统的性能和可靠性提供了良好的支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值