IOCP配合AcceptEx的例子

    技术2022-05-20  32

    这是在学《Windows网络编程(第二版)》第六章时制作的一个例子由于书中没有给出简洁的例子,本人在学这里时就费了很多时间。现在把完成的代码贴出来,供大家参考。下面包括了主程序部分,工作线程在(2)中贴出,由于代码太长。本程序在VS2003编译器编译运行。在6.0下可能需要稍加修改。

    #include <iostream>#include <winsock2.h>#include <ws2tcpip.h>#include <mswsock.h>    //微软扩展的类库

    using namespace std;#define SEND 0#define RECV 1#define ACCEPT 2

    #define DATA_LENGTH 1000//单句柄数据定义typedef struct _PER_HANDLE_DATA{    SOCKET socket;    //相关的套接字    SOCKADDR_STORAGE clientAddr;    //客户端的地址}PER_HANDLE_DATA,*LPPER_HANDLE_DATA;

    //但IO操作数据typedef struct{    OVERLAPPED overlapped;    WSABUF buffer;    //一个数据缓冲区,用于WSASend/WSARecv中的第二个参数    char dataBuffer[DATA_LENGTH];    //实际的数据缓冲区    int dataLength;                    //实际的数据缓冲区长度    int operatorType;                //操作类型,可以为SEND/RECV两种    SOCKET client;                    //分别表示发送的字节数和接收的字节数}PER_IO_DATA,*LPPER_IO_DATA;

    void main(){    HANDLE CompletionPort;    WSADATA data;    SYSTEM_INFO info;    SOCKADDR_IN addr;    SOCKET Listen;

        unsigned int i;    WSAStartup(MAKEWORD(2,2),&data);

        //创建一个IO完成端口    CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);    //确定处理器的数量    GetSystemInfo(&info);    //创建线城    for(i=0;i<info.dwNumberOfProcessors * 2;i++)    {        //根据处理器的数量创建相应多的处理线程        HANDLE thread = CreateThread(NULL,0,ServerThread,CompletionPort,0,NULL);        CloseHandle(thread);    }    //创建一个监听套接字(进行重叠操作)    Listen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);    //将监听套接字与完成端口绑定    LPPER_HANDLE_DATA perDandleData;    perDandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR,sizeof(PER_HANDLE_DATA));    perDandleData->socket = Listen;    CreateIoCompletionPort((HANDLE)Listen,CompletionPort,(ULONG_PTR)perDandleData,0);

        addr.sin_family = AF_INET;    addr.sin_addr.s_addr = htonl(INADDR_ANY);    addr.sin_port = htons(5500);

        bind(Listen,(PSOCKADDR)&addr,sizeof(addr));    listen(Listen,5);

        LPFN_ACCEPTEX lpfnAcceptEx = NULL;    //AcceptEx函数指针    //Accept function GUID    GUID guidAcceptEx = WSAID_ACCEPTEX;    //get acceptex function pointer    DWORD dwBytes = 0;    if(WSAIoctl(Listen,SIO_GET_EXTENSION_FUNCTION_POINTER,        &guidAcceptEx,sizeof(guidAcceptEx),&lpfnAcceptEx,sizeof(lpfnAcceptEx),        &dwBytes,NULL,NULL)==0)        cout<<"WSAIoctl success..."<<endl;    else{        cout<<"WSAIoctl failed..."<<endl;        switch(WSAGetLastError())        {        case WSAENETDOWN:            cout<<""<<endl;            break;        case WSAEFAULT:            cout<<"WSAEFAULT"<<endl;            break;        case WSAEINVAL:            cout<<"WSAEINVAL"<<endl;            break;        case WSAEINPROGRESS:            cout<<"WSAEINPROGRESS"<<endl;            break;        case WSAENOTSOCK:            cout<<"WSAENOTSOCK"<<endl;            break;        case WSAEOPNOTSUPP:            cout<<"WSAEOPNOTSUPP"<<endl;            break;        case WSA_IO_PENDING:            cout<<"WSA_IO_PENDING"<<endl;            break;        case WSAEWOULDBLOCK:            cout<<"WSAEWOULDBLOCK"<<endl;            break;        case WSAENOPROTOOPT:            cout<<"WSAENOPROTOOPT"<<endl;            break;        }        return;    }

        //while(true)    //{        //准备调用 AcceptEx 函数,该函数使用重叠结构并于完成端口连接        LPPER_IO_DATA perIoData = (LPPER_IO_DATA)GlobalAlloc(GPTR,sizeof(PER_IO_DATA));        memset(&(perIoData->overlapped),0,sizeof(OVERLAPPED));            perIoData->operatorType = ACCEPT;        //在使用AcceptEx前需要事先重建一个套接字用于其第二个参数。这样目的是节省时间        //通常可以创建一个套接字库        perIoData->client = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,0,0,WSA_FLAG_OVERLAPPED);

            perIoData->dataLength = DATA_LENGTH;        DWORD flags = 0;                //调用AcceptEx函数,地址长度需要在原有的上面加上16个字节        //注意这里使用了重叠模型,该函数的完成将在与完成端口关联的工作线程中处理        cout<<"Process AcceptEx function wait for client connect..."<<endl;        int rc = lpfnAcceptEx(Listen,perIoData->client,perIoData->dataBuffer,            perIoData->dataLength-((sizeof(SOCKADDR_IN)+16)*2),            sizeof(SOCKADDR_IN)+16,sizeof(SOCKADDR_IN)+16,&dwBytes,            &(perIoData->overlapped));        if(rc == FALSE)        {            if(WSAGetLastError()!=ERROR_IO_PENDING)                cout<<"lpfnAcceptEx failed.."<<endl;        }

            cin>>i;    closesocket(Listen);    WSACleanup();}

    DWORD WINAPI ServerThread(LPVOID lpParam){    HANDLE CompletionPort = (HANDLE)lpParam;    DWORD bytes;    LPPER_HANDLE_DATA perHandleData = NULL;    //单句柄数据    LPPER_IO_DATA perIoData;            //单IO数据    DWORD Flags;    int ret;    DWORD RecvBytes;    //进入循环的等待重叠操作的完成    while(true)    {        bytes = -1;        ret=GetQueuedCompletionStatus(            CompletionPort,                //原先的完成端口句柄            &bytes,                        //重叠操作完成的字节数            (LPDWORD)&perHandleData,    //原先和完成端口句柄关联起来的单句柄数据            (LPOVERLAPPED*)&perIoData,    //用于接收已完成的IO操作的重叠结构            INFINITE);                    //在完成端口上等待的时间 INFINITE 为无限等待                //先检查在套接字上是否发生错误        //当发生错误时关闭套接字同时释放掉所有的内存.        int i = 0;        if(bytes == 0 && (perIoData->operatorType == RECV ||            perIoData->operatorType == SEND))        {            closesocket(perHandleData->socket);            GlobalFree(perHandleData);            GlobalFree(perIoData);            cout<<"closesocket and globalfree perhandledata periodata!"<<endl;            continue;        }        //这是AcceptEx函数处理完成,在下面处理        if(perIoData->operatorType == ACCEPT)    //处理连接操作        {            //使用GetAcceptExSockaddrs函数 获得具体的各个地址参数.            if(setsockopt( perIoData->client, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,                  ( char* )&(perHandleData->socket ), sizeof( perHandleData->socket ) )==SOCKET_ERROR)                cout<<"setsockopt..."<<endl;

                perHandleData->socket = perIoData->client;

                //memcpy(&(perHandleData->clientAddr),raddr,sizeof(raddr));            //将新的客户套接字与完成端口连接            CreateIoCompletionPort((HANDLE)perHandleData->socket,                CompletionPort,(ULONG_PTR)perHandleData,0);

                memset(&(perIoData->overlapped),0,sizeof(OVERLAPPED));            perIoData->operatorType = RECV;        //将状态设置成接收            //设置WSABUF结构            perIoData->buffer.buf = perIoData->dataBuffer;            perIoData->buffer.len = perIoData->dataLength = DATA_LENGTH;

                cout<<"wait for data arrive(Accept)..."<<endl;            Flags = 0;            if(WSARecv(perHandleData->socket,&(perIoData->buffer),1,                &RecvBytes,&Flags,&(perIoData->overlapped),NULL)==SOCKET_ERROR)                if(WSAGetLastError()==WSA_IO_PENDING)                    cout<<"WSARecv Pending..."<<endl;            continue;                    }        if(perIoData->operatorType == RECV)            cout<<perIoData->buffer.buf<<endl;    //将接收到的数据显示出来        Flags = 0;        perIoData->operatorType = RECV;            //设置成接受数据类型

            ZeroMemory(&(perIoData->overlapped),sizeof(OVERLAPPED));        //重新投递一个新的接收请求        cout<<" wait for data arrive..."<<endl;        WSARecv(perHandleData->socket,&(perIoData->buffer),1,            &RecvBytes,&Flags,&(perIoData->overlapped),NULL);    }

        return 0;}

     

    本文来自博客,转载请标明出处:http://blog.csdn.net/kener_linfeng/archive/2009/05/03/4145471.aspx


    最新回复(0)