[網路開發]IOCP完整例子
阿新 • • 發佈:2018-12-17
本文是我在學習IOCP的時候,第一次寫一個完整的例子出來,當然了,參考了CSDN上一些朋友的部落格,大部分都是按照他們的思路寫的,畢竟我是初學者,參考現成的學起來比較快。當然了,真正用到專案中的IOCP肯定不止這麼簡單的,還有記憶體池,環形緩衝區,socket連線池等高階內容,後面我會參考一些例子,寫出一個完整的給大家看。
/************************************************************************ FileName:iocp.h Author :eliteYang http://www.cppfans.org ************************************************************************/ #ifndef __IOCP_H__ #define __IOCP_H__ #include #include #define DefaultPort 20000 #define DataBuffSize 8 * 1024 typedef struct { OVERLAPPED overlapped; WSABUF databuff; CHAR buffer[ DataBuffSize ]; DWORD bytesSend; DWORD bytesRecv; }PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA; typedef struct { SOCKET socket; }PER_HANDLE_DATA, *LPPER_HANDLE_DATA; #endif
前面講過IOCP裡面一個很重要的東西就是IO重疊了,所以結構體裡有一個OVERLAPPED結構。
/************************************************************************ FileName:iocp.cpp Author :eliteYang http://www.cppfans.org ************************************************************************/ #include "iocp.h" #include using namespace std; #pragma comment( lib, "Ws2_32.lib" ) DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID ); void main() { SOCKET acceptSocket; HANDLE completionPort; LPPER_HANDLE_DATA pHandleData; LPPER_IO_OPERATION_DATA pIoData; DWORD recvBytes; DWORD flags; WSADATA wsaData; DWORD ret; if ( ret = WSAStartup( 0x0202, &wsaData ) != 0 ) { std::cout << "WSAStartup failed. Error:" << ret << std::endl; return; } completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0 ); if ( completionPort == NULL ) { std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl; return; } SYSTEM_INFO mySysInfo; GetSystemInfo( &mySysInfo ); // 建立 2 * CPU核數 + 1 個執行緒 DWORD threadID; for ( DWORD i = 0; i < ( mySysInfo.dwNumberOfProcessors * 2 + 1 ); ++i ) { HANDLE threadHandle; threadHandle = CreateThread( NULL, 0, ServerWorkThread, completionPort, 0, &threadID ); if ( threadHandle == NULL ) { std::cout << "CreateThread failed. Error:" << GetLastError() << std::endl; return; } CloseHandle( threadHandle ); } // 啟動一個監聽socket SOCKET listenSocket = WSASocket( AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED ); if ( listenSocket == INVALID_SOCKET ) { std::cout << " WSASocket( listenSocket ) failed. Error:" << GetLastError() << std::endl; return; } SOCKADDR_IN internetAddr; internetAddr.sin_family = AF_INET; internetAddr.sin_addr.s_addr = htonl( INADDR_ANY ); internetAddr.sin_port = htons( DefaultPort ); // 繫結監聽埠 if ( bind( listenSocket, (PSOCKADDR)&internetAddr, sizeof( internetAddr ) ) == SOCKET_ERROR ) { std::cout << "Bind failed. Error:" << GetLastError() << std::endl; return; } if ( listen( listenSocket, 5 ) == SOCKET_ERROR ) { std::cout << "listen failed. Error:" << GetLastError() << std::endl; return; } // 開始死迴圈,處理資料 while ( 1 ) { acceptSocket = WSAAccept( listenSocket, NULL, NULL, NULL, 0 ); if ( acceptSocket == SOCKET_ERROR ) { std::cout << "WSAAccept failed. Error:" << GetLastError() << std::endl; return; } pHandleData = (LPPER_HANDLE_DATA)GlobalAlloc( GPTR, sizeof( PER_HANDLE_DATA ) ); if ( pHandleData = NULL ) { std::cout << "GlobalAlloc( HandleData ) failed. Error:" << GetLastError() << std::endl; return; } pHandleData->socket = acceptSocket; if ( CreateIoCompletionPort( (HANDLE)acceptSocket, completionPort, (ULONG_PTR)pHandleData, 0 ) == NULL ) { std::cout << "CreateIoCompletionPort failed. Error:" << GetLastError() << std::endl; return; } pIoData = ( LPPER_IO_OPERATION_DATA )GlobalAlloc( GPTR, sizeof( PER_IO_OPERATEION_DATA ) ); if ( pIoData == NULL ) { std::cout << "GlobalAlloc( IoData ) failed. Error:" << GetLastError() << std::endl; return; } ZeroMemory( &( pIoData->overlapped ), sizeof( pIoData->overlapped ) ); pIoData->bytesSend = 0; pIoData->bytesRecv = 0; pIoData->databuff.len = DataBuffSize; pIoData->databuff.buf = pIoData->buffer; flags = 0; if ( WSARecv( acceptSocket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR ) { if ( WSAGetLastError() != ERROR_IO_PENDING ) { std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl; return; } else { std::cout << "WSARecv() io pending" << std::endl; return; } } } } DWORD WINAPI ServerWorkThread( LPVOID CompletionPortID ) { HANDLE complationPort = (HANDLE)CompletionPortID; DWORD bytesTransferred; LPPER_HANDLE_DATA pHandleData = NULL; LPPER_IO_OPERATION_DATA pIoData = NULL; DWORD sendBytes = 0; DWORD recvBytes = 0; DWORD flags; while ( 1 ) { if ( GetQueuedCompletionStatus( complationPort, &bytesTransferred, (PULONG_PTR)&pHandleData, (LPOVERLAPPED *)&pIoData, INFINITE ) == 0 ) { std::cout << "GetQueuedCompletionStatus failed. Error:" << GetLastError() << std::endl; return 0; } // 檢查資料是否已經傳輸完了 if ( bytesTransferred == 0 ) { std::cout << " Start closing socket..." << std::endl; if ( CloseHandle( (HANDLE)pHandleData->socket ) == SOCKET_ERROR ) { std::cout << "Close socket failed. Error:" << GetLastError() << std::endl; return 0; } GlobalFree( pHandleData ); GlobalFree( pIoData ); continue; } // 檢查管道里是否有資料 if ( pIoData->bytesRecv == 0 ) { pIoData->bytesRecv = bytesTransferred; pIoData->bytesSend = 0; } else { pIoData->bytesSend += bytesTransferred; } // 資料沒有發完,繼續傳送 if ( pIoData->bytesRecv > pIoData->bytesSend ) { ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) ); pIoData->databuff.buf = pIoData->buffer + pIoData->bytesSend; pIoData->databuff.len = pIoData->bytesRecv - pIoData->bytesSend; // 傳送資料出去 if ( WSASend( pHandleData->socket, &(pIoData->databuff), 1, &sendBytes, 0, &(pIoData->overlapped), NULL ) == SOCKET_ERROR ) { if ( WSAGetLastError() != ERROR_IO_PENDING ) { std::cout << "WSASend() failed. Error:" << GetLastError() << std::endl; return 0; } else { std::cout << "WSASend() failed. io pending. Error:" << GetLastError() << std::endl; return 0; } } std::cout << "Send " << pIoData->buffer << std::endl; } else { pIoData->bytesRecv = 0; flags = 0; ZeroMemory( &(pIoData->overlapped), sizeof( OVERLAPPED ) ); pIoData->databuff.len = DataBuffSize; pIoData->databuff.buf = pIoData->buffer; if ( WSARecv( pHandleData->socket, &(pIoData->databuff), 1, &recvBytes, &flags, &(pIoData->overlapped), NULL ) == SOCKET_ERROR ) { if ( WSAGetLastError() != ERROR_IO_PENDING ) { std::cout << "WSARecv() failed. Error:" << GetLastError() << std::endl; return 0; } else { std::cout << "WSARecv() io pending" << std::endl; return 0; } } } } }
整個過程還是類似於最基礎的socket連線方式,主要部分就是使用IOCP的兩個函式,建立IOCP和檢測當前的狀態。
大家先湊活看吧,後面本部落格會有更精彩的IOCP內容呈現給大家,我也是逐步在學習,大家稍安勿躁。