IOCP配合AcceptEx的例子

IOCP配合AcceptEx的例子

 

本程序在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;
}
在前面有贴出了一个简单的客户端例子,可以结合起来调试,很方便的。

你可能感兴趣的:(职场,acceptex,iocp,休闲)