本程序在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;
}
在前面有貼出了一個簡單的客戶端例子,可以結合起來調試,很方便的。
總結
以上是生活随笔為你收集整理的IOCP配合AcceptEx的例子的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。