简介
完成端口的基本思想和计算机组成原理中的dma非常类似,客户调用overlap操作抛出一个读写请求,然后在工作线程在一个位置(port)等待完成,如果完成,客户根据返回值进行后续的读写操作。在用户抛出异步读写请求以后,就可以转而去做其他事情了。
1,定义异步请求保存数据的结构
enum IO_OPERATION { IO_READ = 1, IO_SEND = 2, IO_ACCEPT = 3, }; struct IO_DATA{ WSAOVERLAPPED Overlapped; // 原子锁 DWORD dwLock; // 上次接受到数据的时间, 判断连接的不活动时间 DWORD dwEnterTick; // 操作代码 IO_OPERATION opCode; // 网络套接字句柄 SOCKET activeSocket; // 网络地址 DWORD dwIpAddr; // 端口 WORD wPort; // 请求类型 char cReqType; //PROXY_HTTP;PROXY_NONE; // 缓冲区 WSABUF wsabuf; // 已经处理的io字节数 DWORD dwRecved; // 缓冲区总字节数 DWORD dwRecvBufTail; // wsaBuf指向的缓冲区 char RecvBuffer[MAX_BUFF_SIZE]; // 已经处理的io字节数 DWORD dwSent; // 缓冲区总字节数 DWORD dwSendBufTail; // wsaBuf指向的缓冲区 char SendBuffer[MAX_BUFF_SIZE]; };
2, 初始化listen
void ListenThread(void * args) { { // Init winsock2 WSADATA wsaData; ZeroMemory(&wsaData,sizeof(WSADATA)); int retVal = -1; if( (retVal = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0 ) { PUTLOG("WSAStartup Failed::Reason Code:: %u\n", retVal); return; } } { // Init Tls Index if (!InitTlsIndex()) return ; } { //Create socket g_ServerSocket = WSASocket(AF_INET,SOCK_STREAM , IPPROTO_TCP , NULL ,0 ,WSA_FLAG_OVERLAPPED); if( g_ServerSocket == INVALID_SOCKET ) { PUTLOG("Server Socket Creation Failed::Reason Code:: %u\n" , WSAGetLastError()); return; } } { //bind sockaddr_in service; service.sin_family = AF_INET; service.sin_addr.s_addr = htonl(INADDR_ANY); service.sin_port = htons(PHONE_PORT); int retVal = bind(g_ServerSocket,(SOCKADDR *)&service,sizeof(service)); if( retVal == SOCKET_ERROR ) { PUTLOG("Server Soket Bind Failed::Reason Code:: %u\n" , WSAGetLastError()); return; } } { //listen int retVal = listen(g_ServerSocket, 100); if( retVal == SOCKET_ERROR ) { PUTLOG("Server Socket Listen Failed::Reason Code:: %u\n" , WSAGetLastError()); return; } PUTLOG("Server Socket Listen at:: %d \n", PHONE_PORT);; } { // Create IOCP SYSTEM_INFO sysInfo; ZeroMemory(&sysInfo,sizeof(SYSTEM_INFO)); GetSystemInfo(&sysInfo); g_ThreadCount = sysInfo.dwNumberOfProcessors + 1; g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,g_ThreadCount); if (g_hIOCP == NULL) { PUTLOG("CreateIoCompletionPort() Failed::Reason:: %u\n" , GetLastError()); return; } if (CreateIoCompletionPort((HANDLE)g_ServerSocket,g_hIOCP,0,0) == NULL) { PUTLOG("Binding Server Socket to IO Completion Port Failed::Reason Code:: %u\n" , GetLastError()); return ; } } { //Create worker threads for( DWORD dwThread=0; dwThread < g_ThreadCount; dwThread++ ) { HANDLE hThread; DWORD dwThreadId; hThread = CreateThread(NULL, 0, WorkerThread, 0, 0, &dwThreadId); CloseHandle(hThread); } } { // accept new connection // Load the AcceptEx function into memory using WSAIoctl. // The WSAIoctl function is an extension of the ioctlsocket() // function that can use overlapped I/O. The function's 3rd // through 6th parameters are input and output buffers where // we pass the pointer to our AcceptEx function. This is used // so that we can call the AcceptEx function directly, rather // than refer to the Mswsock.lib library. GUID GuidAcceptEx = WSAID_ACCEPTEX; DWORD dwBytes = 0; int iResult = WSAIoctl(g_ServerSocket , SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof (GuidAcceptEx), &g_lpfnAcceptEx, sizeof (g_lpfnAcceptEx), &dwBytes, NULL, NULL); if (iResult == SOCKET_ERROR) { wprintf(L"WSAIoctl failed with error: %u\n", WSAGetLastError()); closesocket(g_ServerSocket); WSACleanup(); return ; } // 投递accept操作 enum { nMaxAcceptRequest = 1, }; IO_DATA * pIoData = new IO_DATA [nMaxAcceptRequest]; memset(pIoData, 0, sizeof(IO_DATA)*nMaxAcceptRequest); for (int xReq = 0; xReq < nMaxAcceptRequest; xReq++) { if (!PostAcceptRequest(pIoData + xReq)) break; } //END OF POST ACCEPT } }
2.a 投递accept请求
bool PostAcceptRequest(IO_DATA * pIoData) { DWORD dwBytes = 0; // Create an accepting socket SOCKET AcceptSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (AcceptSocket == INVALID_SOCKET) { PUTLOG("Create accept socket failed with error: %u\n" , WSAGetLastError()); return false; } // Empty our overlapped structure and accept connections. memset(&pIoData->Overlapped, 0, sizeof(pIoData->Overlapped)); pIoData->opCode = IO_ACCEPT; pIoData->activeSocket = AcceptSocket; BOOL bRetVal = g_lpfnAcceptEx(g_ServerSocket , AcceptSocket , pIoData->RecvBuffer , 0 //, sizeof(pIoData[nCocurrent].Buffer) // -((sizeof(sockaddr_in)+16)*2) , sizeof (sockaddr_in) + 16 , sizeof (sockaddr_in) + 16 , &dwBytes , &pIoData->Overlapped); if (bRetVal == FALSE && GetLastError() != ERROR_IO_PENDING) { PUTLOG("AcceptEx failed with error: %u\n" , WSAGetLastError()); closesocket(AcceptSocket); return false; } // associate if (NULL == (CreateIoCompletionPort((HANDLE) AcceptSocket , g_hIOCP , (u_long) 0 , 0))) { PUTLOG("CreateIoCompletionPort associate failed with error: %u\n" , GetLastError()); closesocket(AcceptSocket); return false; } return true; }
bool PostRecvRequest(IO_DATA * const ioReq, SOCKET sockfd) { IO_DATA * data = ioReq; data->activeSocket = sockfd; ZeroMemory(&data->Overlapped,sizeof(data->Overlapped)); data->opCode = IO_OPERATION(IO_READ); data->wsabuf.buf = data->RecvBuffer + data->dwRecved; data->dwRecvBufTail = sizeof(data->RecvBuffer); data->wsabuf.len = data->dwRecvBufTail - data->dwRecved; DWORD dwRecvNumBytes=0; DWORD dwFlags=(MSG_PEEK & (~MSG_PEEK)); int nRet = WSARecv(data->activeSocket , &data->wsabuf , 1 , &dwRecvNumBytes , &dwFlags , &data->Overlapped , NULL); if(nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())) { PUTLOG("WASRecv Failed::Reason Code:: %u\n" , WSAGetLastError()); return false; } return true; }
3.c 抛出send请求
bool PostSendRequest(IO_DATA * const ioReq, SOCKET sockfd) { IO_DATA * data = ioReq; data->activeSocket = sockfd; ZeroMemory(&data->Overlapped,sizeof(data->Overlapped)); data->opCode = IO_OPERATION(IO_SEND); data->wsabuf.buf = data->SendBuffer + data->dwSent; data->wsabuf.len = data->dwSendBufTail - data->dwSent; DWORD dwSendNumBytes=0; DWORD dwFlags=(MSG_PEEK & (~MSG_PEEK)); int nRet = WSASend(data->activeSocket , &data->wsabuf , 1 , &dwSendNumBytes , dwFlags , &data->Overlapped , NULL); if(nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError())) { PUTLOG("WSASend Failed::Reason Code:: %u\n" , WSAGetLastError()); return false; } return true; }
DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) { for (;;) { BOOL bSuccess = FALSE; DWORD dwIoSize = 0; void * lpCompletionKey = NULL; LPWSAOVERLAPPED lpOverlapped = NULL; bSuccess = GetQueuedCompletionStatus(g_hIOCP , &dwIoSize , (LPDWORD)&lpCompletionKey , (LPOVERLAPPED *)&lpOverlapped , INFINITE); if(!bSuccess) { PUTLOG("GetQueuedCompletionStatus() failed: %u\n", GetLastError()); } IO_DATA * lpIOContext = (IO_DATA *)lpOverlapped; // 新的套接字连接上来 if(lpIOContext->opCode == IO_ACCEPT) // a read operation complete { // 更新AccpetEx返回的socket状态 int ErrorCode= setsockopt(lpIOContext->activeSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&g_ServerSocket, sizeof(g_ServerSocket)); // 提交读取请求 IO_DATA * data = new IO_DATA; if (data) { PostRecvRequest(data, lpIOContext->activeSocket); } else { closesocket(data->activeSocket); data->activeSocket = INVALID_SOCKET; delete data; } // 提交一个新的accept PostAcceptRequest(lpIOContext); continue ; } if(dwIoSize == 0) //socket closed? { PUTLOG("Client Dis-connected.\n"); closesocket(lpIOContext->activeSocket); lpIOContext->activeSocket = INVALID_SOCKET; continue; } // 读取完毕 if(lpIOContext->opCode == IO_READ) { // 重置超时 lpIOContext->dwEnterTick = GetTickCount(); // 累加总请求长度 lpIOContext->dwRecved += dwIoSize; bool IsReqReady = true; // // 如果数据包读取完毕 // 开始处理 // 处理完投递发送请求 // // ... if (IsReqReady) { lpIOContext->dwSent = 0; lpIOContext->dwSendBufTail = 0; memcpy(lpIOContext->SendBuffer + lpIOContext->dwSendBufTail , "\r\n\r\nServerEcho: " , 16); lpIOContext->dwSendBufTail += 16; memcpy(lpIOContext->SendBuffer + lpIOContext->dwSendBufTail , lpIOContext->RecvBuffer , lpIOContext->dwRecved); lpIOContext->dwSendBufTail += lpIOContext->dwRecved; memcpy(lpIOContext->SendBuffer + lpIOContext->dwSendBufTail , "\r\n\r\n" , 4); lpIOContext->dwSendBufTail += 4; PostSendRequest(lpIOContext, lpIOContext->activeSocket); } // // 如果没有读取完毕 // 继续投递读取请求 // // ... else { PostRecvRequest(lpIOContext, lpIOContext->activeSocket); } continue ; } // 发送完毕 if (lpIOContext->opCode == IO_SEND) { // 重置超时 lpIOContext->dwEnterTick = GetTickCount(); lpIOContext->dwSent += dwIoSize; bool IsRespReady = (lpIOContext->dwSent == lpIOContext->dwSendBufTail); // // 如果数据包发送完毕 // 开始回话后处理 // 处理完投递读取请求 // // ... if (IsRespReady) { lpIOContext->dwRecved = 0; lpIOContext->dwRecvBufTail = sizeof(lpIOContext->RecvBuffer); PostRecvRequest(lpIOContext, lpIOContext->activeSocket); } // // 如果没有发送完毕 // 继续投递发送请求 // // ... else { PostSendRequest(lpIOContext, lpIOContext->activeSocket); } continue ; } } return 0; }
int main(int argc, char * argv []) { ListenThread(NULL); PUTLOG("Press any key to quit server."); getchar(); return 0; }
=====================================================================
项目文件下载:
http://hi.csdn.net/attachment/201112/26/0_1324859981Zd6C.gif