iopc源碼

  1. #include <WinSock2.h>  
  2. #include <Windows.h>  
  3. #include <vector>  
  4. #include <iostream>  
  5.   
  6. using namespace std;  
  7.   
  8. #pragma comment(lib, "Ws2_32.lib")      // Socket編程需用的動態鏈接庫  
  9. #pragma comment(lib, "Kernel32.lib")    // IOCP需要用到的動態鏈接庫  
  10.   
  11. /** 
  12.  * 結構體名稱:PER_IO_DATA 
  13.  * 結構體功能:重疊I/O需要用到的結構體,臨時記錄IO數據 
  14.  **/  
  15. const int DataBuffSize  = 2 * 1024;  
  16. typedef struct  
  17. {  
  18.     OVERLAPPED overlapped;  
  19.     WSABUF databuff;  
  20.     char buffer[ DataBuffSize ];  
  21.     int BufferLen;  
  22.     int operationType;  
  23. }PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;  
  24.   
  25. /** 
  26.  * 結構體名稱:PER_HANDLE_DATA 
  27.  * 結構體存儲:記錄單個套接字的數據,包括了套接字的變量及套接字的對應的客戶端的地址。 
  28.  * 結構體作用:當服務器連接上客戶端時,信息存儲到該結構體中,知道客戶端的地址以便於回訪。 
  29.  **/  
  30. typedef struct  
  31. {  
  32.     SOCKET socket;  
  33.     SOCKADDR_STORAGE ClientAddr;  
  34. }PER_HANDLE_DATA, *LPPER_HANDLE_DATA;  
  35.   
  36. // 定義全局變量  
  37. const int DefaultPort = 6000;         
  38. vector < PER_HANDLE_DATA* > clientGroup;      // 記錄客戶端的向量組  
  39.   
  40. HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);  
  41. DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID);  
  42. DWORD WINAPI ServerSendThread(LPVOID IpParam);  
  43.   
  44. // 開始主函數  
  45. int main()  
  46. {  
  47. // 加載socket動態鏈接庫  
  48.     WORD wVersionRequested = MAKEWORD(2, 2); // 請求2.2版本的WinSock庫  
  49.     WSADATA wsaData;    // 接收Windows Socket的結構信息  
  50.     DWORD err = WSAStartup(wVersionRequested, &wsaData);  
  51.   
  52.     if (0 != err){  // 檢查套接字庫是否申請成功  
  53.         cerr << "Request Windows Socket Library Error!\n";  
  54.         system("pause");  
  55.         return -1;  
  56.     }  
  57.     if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){// 檢查是否申請了所需版本的套接字庫  
  58.         WSACleanup();  
  59.         cerr << "Request Windows Socket Version 2.2 Error!\n";  
  60.         system("pause");  
  61.         return -1;  
  62.     }  
  63.   
  64. // 創建IOCP的內核對象  
  65.     /** 
  66.      * 需要用到的函數的原型: 
  67.      * HANDLE WINAPI CreateIoCompletionPort( 
  68.      *    __in   HANDLE FileHandle,     // 已經打開的文件句柄或者空句柄,一般是客戶端的句柄 
  69.      *    __in   HANDLE ExistingCompletionPort, // 已經存在的IOCP句柄 
  70.      *    __in   ULONG_PTR CompletionKey,   // 完成鍵,包含了指定I/O完成包的指定文件 
  71.      *    __in   DWORD NumberOfConcurrentThreads // 真正併發同時執行最大線程數,一般推介是CPU核心數*2 
  72.      * ); 
  73.      **/  
  74.     HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0);  
  75.     if (NULL == completionPort){    // 創建IO內核對象失敗  
  76.         cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;  
  77.         system("pause");  
  78.         return -1;  
  79.     }  
  80.   
  81. // 創建IOCP線程--線程裏面創建線程池  
  82.   
  83.     // 確定處理器的核心數量  
  84.     SYSTEM_INFO mySysInfo;  
  85.     GetSystemInfo(&mySysInfo);  
  86.   
  87.     // 基於處理器的核心數量創建線程  
  88.     for(DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i){  
  89.         // 創建服務器工作器線程,並將完成端口傳遞到該線程  
  90.         HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);  
  91.         if(NULL == ThreadHandle){  
  92.             cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;  
  93.         system("pause");  
  94.             return -1;  
  95.         }  
  96.         CloseHandle(ThreadHandle);  
  97.     }  
  98.   
  99. // 建立流式套接字  
  100.     SOCKET srvSocket = socket(AF_INET, SOCK_STREAM, 0);  
  101.   
  102. // 綁定SOCKET到本機  
  103.     SOCKADDR_IN srvAddr;  
  104.     srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  105.     srvAddr.sin_family = AF_INET;  
  106.     srvAddr.sin_port = htons(DefaultPort);  
  107.     int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));  
  108.     if(SOCKET_ERROR == bindResult){  
  109.         cerr << "Bind failed. Error:" << GetLastError() << endl;  
  110.         system("pause");  
  111.         return -1;  
  112.     }  
  113.   
  114. // 將SOCKET設置爲監聽模式  
  115.     int listenResult = listen(srvSocket, 10);  
  116.     if(SOCKET_ERROR == listenResult){  
  117.         cerr << "Listen failed. Error: " << GetLastError() << endl;  
  118.         system("pause");  
  119.         return -1;  
  120.     }  
  121.       
  122. // 開始處理IO數據  
  123.     cout << "本服務器已準備就緒,正在等待客戶端的接入...\n";  
  124.   
  125.     // 創建用於發送數據的線程  
  126.     HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);  
  127.   
  128.     while(true){  
  129.         PER_HANDLE_DATA * PerHandleData = NULL;  
  130.         SOCKADDR_IN saRemote;  
  131.         int RemoteLen;  
  132.         SOCKET acceptSocket;  
  133.   
  134.         // 接收連接,並分配完成端,這兒可以用AcceptEx()  
  135.         RemoteLen = sizeof(saRemote);  
  136.         acceptSocket = accept(srvSocket, (SOCKADDR*)&saRemote, &RemoteLen);  
  137.         if(SOCKET_ERROR == acceptSocket){   // 接收客戶端失敗  
  138.             cerr << "Accept Socket Error: " << GetLastError() << endl;  
  139.             system("pause");  
  140.             return -1;  
  141.         }  
  142.           
  143.         // 創建用來和套接字關聯的單句柄數據信息結構  
  144.         PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));  // 在堆中爲這個PerHandleData申請指定大小的內存  
  145.         PerHandleData -> socket = acceptSocket;  
  146.         memcpy (&PerHandleData -> ClientAddr, &saRemote, RemoteLen);  
  147.         clientGroup.push_back(PerHandleData);       // 將單個客戶端數據指針放到客戶端組中  
  148.   
  149.         // 將接受套接字和完成端口關聯  
  150.         CreateIoCompletionPort((HANDLE)(PerHandleData -> socket), completionPort, (DWORD)PerHandleData, 0);  
  151.   
  152.           
  153.         // 開始在接受套接字上處理I/O使用重疊I/O機制  
  154.         // 在新建的套接字上投遞一個或多個異步  
  155.         // WSARecv或WSASend請求,這些I/O請求完成後,工作者線程會爲I/O請求提供服務      
  156.         // 單I/O操作數據(I/O重疊)  
  157.         LPPER_IO_OPERATION_DATA PerIoData = NULL;  
  158.         PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));  
  159.         ZeroMemory(&(PerIoData -> overlapped), sizeof(OVERLAPPED));  
  160.         PerIoData->databuff.len = 1024;  
  161.         PerIoData->databuff.buf = PerIoData->buffer;  
  162.         PerIoData->operationType = 0;    // read  
  163.   
  164.         DWORD RecvBytes;  
  165.         DWORD Flags = 0;  
  166.         WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);  
  167.     }  
  168.   
  169.     system("pause");  
  170.     return 0;  
  171. }  
  172.   
  173. // 開始服務工作線程函數  
  174. DWORD WINAPI ServerWorkThread(LPVOID IpParam)  
  175. {  
  176.     HANDLE CompletionPort = (HANDLE)IpParam;  
  177.     DWORD BytesTransferred;  
  178.     LPOVERLAPPED IpOverlapped;  
  179.     LPPER_HANDLE_DATA PerHandleData = NULL;  
  180.     LPPER_IO_DATA PerIoData = NULL;  
  181.     DWORD RecvBytes;  
  182.     DWORD Flags = 0;  
  183.     BOOL bRet = false;  
  184.   
  185.     while(true){  
  186.         bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);  
  187.         if(bRet == 0){  
  188.             cerr << "GetQueuedCompletionStatus Error: " << GetLastError() << endl;  
  189.             return -1;  
  190.         }  
  191.         PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);  
  192.           
  193.         // 檢查在套接字上是否有錯誤發生  
  194.         if(0 == BytesTransferred){  
  195.             closesocket(PerHandleData->socket);  
  196.             GlobalFree(PerHandleData);  
  197.             GlobalFree(PerIoData);  
  198.             continue;  
  199.         }  
  200.           
  201.         // 開始數據處理,接收來自客戶端的數據  
  202.         WaitForSingleObject(hMutex,INFINITE);  
  203.         cout << "A Client says: " << PerIoData->databuff.buf << endl;  
  204.         ReleaseMutex(hMutex);  
  205.   
  206.         // 爲下一個重疊調用建立單I/O操作數據  
  207.         ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空內存  
  208.         PerIoData->databuff.len = 1024;  
  209.         PerIoData->databuff.buf = PerIoData->buffer;  
  210.         PerIoData->operationType = 0;    // read  
  211.         WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);  
  212.     }  
  213.   
  214.     return 0;  
  215. }  
  216.   
  217.   
  218. // 發送信息的線程執行函數  
  219. DWORD WINAPI ServerSendThread(LPVOID IpParam)  
  220. {  
  221.     while(1){  
  222.         char talk[200];  
  223.         gets(talk);  
  224.         int len;  
  225.         for (len = 0; talk[len] != '\0'; ++len){  
  226.             // 找出這個字符組的長度  
  227.         }  
  228.         talk[len] = '\n';  
  229.         talk[++len] = '\0';  
  230.         printf("I Say:");  
  231.         cout << talk;  
  232.         WaitForSingleObject(hMutex,INFINITE);  
  233.         for(int i = 0; i < clientGroup.size(); ++i){  
  234.             send(clientGroup[i]->socket, talk, 200, 0);  // 發送信息  
  235.         }  
  236.         ReleaseMutex(hMutex);   
  237.     }  
  238.     return 0;  
  239. }  


  1. // IOCP_TCPIP_Socket_Client.cpp  
  2.   
  3. #include <iostream>  
  4. #include <cstdio>  
  5. #include <string>  
  6. #include <cstring>  
  7. #include <winsock2.h>  
  8. #include <Windows.h>  
  9.   
  10. using namespace std;  
  11.   
  12. #pragma comment(lib, "Ws2_32.lib")      // Socket編程需用的動態鏈接庫  
  13.   
  14. SOCKET sockClient;      // 連接成功後的套接字  
  15. HANDLE bufferMutex;     // 令其能互斥成功正常通信的信號量句柄  
  16. const int DefaultPort = 6000;  
  17.   
  18. int main()  
  19. {  
  20. // 加載socket動態鏈接庫(dll)  
  21.     WORD wVersionRequested;  
  22.     WSADATA wsaData;    // 這結構是用於接收Wjndows Socket的結構信息的  
  23.     wVersionRequested = MAKEWORD( 2, 2 );   // 請求2.2版本的WinSock庫  
  24.     int err = WSAStartup( wVersionRequested, &wsaData );  
  25.     if ( err != 0 ) {   // 返回值爲零的時候是表示成功申請WSAStartup  
  26.         return -1;  
  27.     }  
  28.     if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { // 檢查版本號是否正確  
  29.         WSACleanup( );  
  30.         return -1;   
  31.     }  
  32.       
  33. // 創建socket操作,建立流式套接字,返回套接字號sockClient  
  34.      sockClient = socket(AF_INET, SOCK_STREAM, 0);  
  35.      if(sockClient == INVALID_SOCKET) {   
  36.         printf("Error at socket():%ld\n", WSAGetLastError());   
  37.         WSACleanup();   
  38.         return -1;   
  39.       }   
  40.   
  41. // 將套接字sockClient與遠程主機相連  
  42.     // int connect( SOCKET s,  const struct sockaddr* name,  int namelen);  
  43.     // 第一個參數:需要進行連接操作的套接字  
  44.     // 第二個參數:設定所需要連接的地址信息  
  45.     // 第三個參數:地址的長度  
  46.     SOCKADDR_IN addrSrv;  
  47.     addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");      // 本地迴路地址是127.0.0.1;   
  48.     addrSrv.sin_family = AF_INET;  
  49.     addrSrv.sin_port = htons(DefaultPort);  
  50.     while(SOCKET_ERROR == connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))){  
  51.         // 如果還沒連接上服務器則要求重連  
  52.         cout << "服務器連接失敗,是否重新連接?(Y/N):";  
  53.         char choice;  
  54.         while(cin >> choice && (!((choice != 'Y' && choice == 'N') || (choice == 'Y' && choice != 'N')))){  
  55.             cout << "輸入錯誤,請重新輸入:";  
  56.             cin.sync();  
  57.             cin.clear();  
  58.         }  
  59.         if (choice == 'Y'){  
  60.             continue;  
  61.         }  
  62.         else{  
  63.             cout << "退出系統中...";  
  64.             system("pause");  
  65.             return 0;  
  66.         }  
  67.     }  
  68.     cin.sync();  
  69.     cout << "本客戶端已準備就緒,用戶可直接輸入文字向服務器反饋信息。\n";  
  70.   
  71.     send(sockClient, "\nAttention: A Client has enter...\n", 200, 0);  
  72.   
  73.     bufferMutex = CreateSemaphore(NULL, 1, 1, NULL);   
  74.   
  75.     DWORD WINAPI SendMessageThread(LPVOID IpParameter);  
  76.     DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter);  
  77.   
  78.     HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL);    
  79.     HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, NULL, 0, NULL);    
  80.   
  81.          
  82.     WaitForSingleObject(sendThread, INFINITE);  // 等待線程結束  
  83.     closesocket(sockClient);  
  84.     CloseHandle(sendThread);  
  85.     CloseHandle(receiveThread);  
  86.     CloseHandle(bufferMutex);  
  87.     WSACleanup();   // 終止對套接字庫的使用  
  88.   
  89.     printf("End linking...\n");  
  90.     printf("\n");  
  91.     system("pause");  
  92.     return 0;  
  93. }  
  94.   
  95.   
  96. DWORD WINAPI SendMessageThread(LPVOID IpParameter)  
  97. {  
  98.     while(1){  
  99.         string talk;  
  100.         getline(cin, talk);  
  101.         WaitForSingleObject(bufferMutex, INFINITE);     // P(資源未被佔用)    
  102.         if("quit" == talk){  
  103.             talk.push_back('\0');  
  104.             send(sockClient, talk.c_str(), 200, 0);  
  105.             break;  
  106.         }  
  107.         else{  
  108.             talk.append("\n");  
  109.         }  
  110.         printf("\nI Say:(\"quit\"to exit):");  
  111.         cout << talk;  
  112.         send(sockClient, talk.c_str(), 200, 0); // 發送信息  
  113.         ReleaseSemaphore(bufferMutex, 1, NULL);     // V(資源佔用完畢)   
  114.     }  
  115.     return 0;  
  116. }  
  117.   
  118.   
  119. DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter)  
  120. {  
  121.     while(1){     
  122.         char recvBuf[300];  
  123.         recv(sockClient, recvBuf, 200, 0);  
  124.         WaitForSingleObject(bufferMutex, INFINITE);     // P(資源未被佔用)    
  125.   
  126.         printf("%s Says: %s""Server", recvBuf);       // 接收信息  
  127.           
  128.         ReleaseSemaphore(bufferMutex, 1, NULL);     // V(資源佔用完畢)   
  129.     }  
  130.     return 0;  
  131. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章