日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

windows IOCP模型

發(fā)布時(shí)間:2025/3/15 windows 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 windows IOCP模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


IOCP模型與網(wǎng)絡(luò)編程

一。前言:
??????? 在老師分配任務(wù)(“嘗試?yán)肐OCP模型寫出服務(wù)端和客戶端的代碼”)給我時(shí),腦子一片空白,并不知道什么是IOCP模型,會(huì)不會(huì)是像軟件設(shè)計(jì)模式里面的工廠模式,裝飾模式之類的那些呢?嘿嘿,不過好像是一個(gè)挺好玩的東西,挺好奇是什么東西來的,又是一個(gè)新知識(shí)啦~于是,開始去尋找一大堆的資料,為這個(gè)了解做準(zhǔn)備,只是呢,有時(shí)還是想去找一本書去系統(tǒng)地學(xué)習(xí)一下,畢竟網(wǎng)絡(luò)的資料還是有點(diǎn)零散。話說,本人學(xué)習(xí)這個(gè)模型的基礎(chǔ)是,寫過一個(gè)簡(jiǎn)單的Socket服務(wù)器及客戶端程序,外加一個(gè)簡(jiǎn)單的Socket單服務(wù)器對(duì)多客戶端程序,懂一點(diǎn)點(diǎn)的操作系統(tǒng)原理的知識(shí)。于是,本著一個(gè)學(xué)習(xí)與應(yīng)用的態(tài)度開始探究這個(gè)IOCP是個(gè)什么東西。

?

二。提出相關(guān)問題:
???????1.? IOCP模型是什么?
???????2.? IOCP模型是用來解決什么問題的?它為什么存在?
???????3.? 使用IOCP模型需要用到哪些知識(shí)?
???????4.? 如何使用IOCP模型與Socket網(wǎng)絡(luò)編程結(jié)合起來?
???????5.? 學(xué)會(huì)了這個(gè)模型以后與我之前寫過的簡(jiǎn)單的socket程序主要有哪些不同點(diǎn)?

?

三。部分問題探究及解決:(絕大多數(shù)是個(gè)人理解,再加上個(gè)人是菜鳥,如果有什么不對(duì)的地方,歡迎指正)
???????1.? 什么是IOCP?什么是IOCP模型?IOCP模型有什么作用?
??????????????1) IOCP(I/O Completion Port),常稱I/O完成端口。
??????????????2) IOCP模型屬于一種通訊模型,適用于(能控制并發(fā)執(zhí)行的)高負(fù)載服務(wù)器的一個(gè)技術(shù)。
??????????????3) 通俗一點(diǎn)說,就是用于高效處理很多很多的客戶端進(jìn)行數(shù)據(jù)交換的一個(gè)模型。
??????????????4) 或者可以說,就是能異步I/O操作的模型。
??????????????5) 只是了解到這些會(huì)讓人很糊涂,因?yàn)檫€是不知道它究意具體是個(gè)什么東東呢?


下面我想給大家看三個(gè)圖:
第一個(gè)是IOCP的內(nèi)部工作隊(duì)列圖。(整合于《IOCP本質(zhì)論》文章,在英文的基礎(chǔ)上加上中文對(duì)照)
?

第二個(gè)是程序?qū)崿F(xiàn)IOCP模型的基本步驟。(整合于《深入解釋IOCP》,加個(gè)人觀點(diǎn)、理解、翻譯)
?
?

第三個(gè)是使用了IOCP模型及沒使用IOCP模型的程序流程圖。(個(gè)人理解繪制)
?

?

2.? IOCP的存在理由(IOCP的優(yōu)點(diǎn))及技術(shù)相關(guān)有哪些?
??????? 之前說過,很通俗地理解可以理解成是用于高效處理很多很多的客戶端進(jìn)行數(shù)據(jù)交換的一個(gè)模型,那么,它具體的優(yōu)點(diǎn)有些什么呢?它到底用到了哪些技術(shù)了呢?在Windows環(huán)境下又如何去使用這些技術(shù)來編程呢?它主要使用上哪些API函數(shù)呢?呃~看來我真是一個(gè)問題多多的人,跟前面提出的相關(guān)問題變種延伸了不少的問題,好吧,下面一個(gè)個(gè)來解決。

?

1) 使用IOCP模型編程的優(yōu)點(diǎn)
???????① 幫助維持重復(fù)使用的內(nèi)存池。(與重疊I/O技術(shù)有關(guān))
???????② 去除刪除線程創(chuàng)建/終結(jié)負(fù)擔(dān)。
???????③ 利于管理,分配線程,控制并發(fā),最小化的線程上下文切換。
???????④ 優(yōu)化線程調(diào)度,提高CPU和內(nèi)存緩沖的命中率。

2) 使用IOCP模型編程汲及到的知識(shí)點(diǎn)(無先后順序)
?????? ① 同步與異步
???????② 阻塞與非阻塞
???????③ 重疊I/O技術(shù)
???????④ 多線程
???????⑤ 棧、隊(duì)列這兩種基本的數(shù)據(jù)結(jié)構(gòu)

3) 需要使用上的API函數(shù)
? ① 與SOCKET相關(guān)
???????1、鏈接套接字動(dòng)態(tài)鏈接庫(kù):int WSAStartup(...);
???????2、創(chuàng)建套接字庫(kù):??????? SOCKET socket(...);
???????3、綁字套接字:????????? int bind(...);
???????4、套接字設(shè)為監(jiān)聽狀態(tài): int listen(...);
???????5、接收套接字:????????? SOCKET accept(...);
???????6、向指定套接字發(fā)送信息:int send(...);
???????7、從指定套接字接收信息:int recv(...);

? ② 與線程相關(guān)
?????? 1、創(chuàng)建線程:HANDLE CreateThread(...);

? ③ 重疊I/O技術(shù)相關(guān)
???????1、向套接字發(fā)送數(shù)據(jù):??? int WSASend(...);
???????2、向套接字發(fā)送數(shù)據(jù)包:? int WSASendFrom(...);
???????3、從套接字接收數(shù)據(jù):??? int WSARecv(...);
???????4、從套接字接收數(shù)據(jù)包:? int WSARecvFrom(...);

? ④ IOCP相關(guān)
???????1、創(chuàng)建完成端口: HANDLE WINAPI CreateIoCompletionPort(...);
???????2、關(guān)聯(lián)完成端口: HANDLE WINAPI CreateIoCompletionPort(...);
???????3、獲取隊(duì)列完成狀態(tài): BOOL WINAPI GetQueuedCompletionStatus(...);
???????4、投遞一個(gè)隊(duì)列完成狀態(tài):BOOL WINAPI PostQueuedCompletionStatus(...);

?

四。完整的簡(jiǎn)單的IOCP服務(wù)器與客戶端代碼實(shí)例:

?

// IOCP_TCPIP_Socket_Server.cpp#include <WinSock2.h> #include <Windows.h> #include <vector> #include <iostream>using namespace std;#pragma comment(lib, "Ws2_32.lib") // Socket編程需用的動(dòng)態(tài)鏈接庫(kù) #pragma comment(lib, "Kernel32.lib") // IOCP需要用到的動(dòng)態(tài)鏈接庫(kù)/*** 結(jié)構(gòu)體名稱:PER_IO_DATA* 結(jié)構(gòu)體功能:重疊I/O需要用到的結(jié)構(gòu)體,臨時(shí)記錄IO數(shù)據(jù)**/ const int DataBuffSize = 2 * 1024; typedef struct {OVERLAPPED overlapped;WSABUF databuff;char buffer[ DataBuffSize ];int BufferLen;int operationType; }PER_IO_OPERATEION_DATA, *LPPER_IO_OPERATION_DATA, *LPPER_IO_DATA, PER_IO_DATA;/*** 結(jié)構(gòu)體名稱:PER_HANDLE_DATA* 結(jié)構(gòu)體存儲(chǔ):記錄單個(gè)套接字的數(shù)據(jù),包括了套接字的變量及套接字的對(duì)應(yīng)的客戶端的地址。* 結(jié)構(gòu)體作用:當(dāng)服務(wù)器連接上客戶端時(shí),信息存儲(chǔ)到該結(jié)構(gòu)體中,知道客戶端的地址以便于回訪。**/ typedef struct {SOCKET socket;SOCKADDR_STORAGE ClientAddr; }PER_HANDLE_DATA, *LPPER_HANDLE_DATA;// 定義全局變量 const int DefaultPort = 6000; vector < PER_HANDLE_DATA* > clientGroup; // 記錄客戶端的向量組HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); DWORD WINAPI ServerWorkThread(LPVOID CompletionPortID); DWORD WINAPI ServerSendThread(LPVOID IpParam);// 開始主函數(shù) int main() { // 加載socket動(dòng)態(tài)鏈接庫(kù)WORD wVersionRequested = MAKEWORD(2, 2); // 請(qǐng)求2.2版本的WinSock庫(kù)WSADATA wsaData; // 接收Windows Socket的結(jié)構(gòu)信息DWORD err = WSAStartup(wVersionRequested, &wsaData);if (0 != err){ // 檢查套接字庫(kù)是否申請(qǐng)成功cerr << "Request Windows Socket Library Error!\n";system("pause");return -1;}if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){// 檢查是否申請(qǐng)了所需版本的套接字庫(kù)WSACleanup();cerr << "Request Windows Socket Version 2.2 Error!\n";system("pause");return -1;}// 創(chuàng)建IOCP的內(nèi)核對(duì)象/*** 需要用到的函數(shù)的原型:* HANDLE WINAPI CreateIoCompletionPort(* __in HANDLE FileHandle, // 已經(jīng)打開的文件句柄或者空句柄,一般是客戶端的句柄* __in HANDLE ExistingCompletionPort, // 已經(jīng)存在的IOCP句柄* __in ULONG_PTR CompletionKey, // 完成鍵,包含了指定I/O完成包的指定文件* __in DWORD NumberOfConcurrentThreads // 真正并發(fā)同時(shí)執(zhí)行最大線程數(shù),一般推介是CPU核心數(shù)*2* );**/HANDLE completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, 0);if (NULL == completionPort){ // 創(chuàng)建IO內(nèi)核對(duì)象失敗cerr << "CreateIoCompletionPort failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 創(chuàng)建IOCP線程--線程里面創(chuàng)建線程池// 確定處理器的核心數(shù)量SYSTEM_INFO mySysInfo;GetSystemInfo(&mySysInfo);// 基于處理器的核心數(shù)量創(chuàng)建線程for(DWORD i = 0; i < (mySysInfo.dwNumberOfProcessors * 2); ++i){// 創(chuàng)建服務(wù)器工作器線程,并將完成端口傳遞到該線程HANDLE ThreadHandle = CreateThread(NULL, 0, ServerWorkThread, completionPort, 0, NULL);if(NULL == ThreadHandle){cerr << "Create Thread Handle failed. Error:" << GetLastError() << endl;system("pause");return -1;}CloseHandle(ThreadHandle);}// 建立流式套接字SOCKET srvSocket = socket(AF_INET, SOCK_STREAM, 0);// 綁定SOCKET到本機(jī)SOCKADDR_IN srvAddr;srvAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);srvAddr.sin_family = AF_INET;srvAddr.sin_port = htons(DefaultPort);int bindResult = bind(srvSocket, (SOCKADDR*)&srvAddr, sizeof(SOCKADDR));if(SOCKET_ERROR == bindResult){cerr << "Bind failed. Error:" << GetLastError() << endl;system("pause");return -1;}// 將SOCKET設(shè)置為監(jiān)聽模式int listenResult = listen(srvSocket, 10);if(SOCKET_ERROR == listenResult){cerr << "Listen failed. Error: " << GetLastError() << endl;system("pause");return -1;}// 開始處理IO數(shù)據(jù)cout << "本服務(wù)器已準(zhǔn)備就緒,正在等待客戶端的接入...\n";// 創(chuàng)建用于發(fā)送數(shù)據(jù)的線程HANDLE sendThread = CreateThread(NULL, 0, ServerSendThread, 0, 0, NULL);while(true){PER_HANDLE_DATA * PerHandleData = NULL;SOCKADDR_IN saRemote;int RemoteLen;SOCKET acceptSocket;// 接收連接,并分配完成端,這兒可以用AcceptEx()RemoteLen = sizeof(saRemote);acceptSocket = accept(srvSocket, (SOCKADDR*)&saRemote, &RemoteLen);if(SOCKET_ERROR == acceptSocket){ // 接收客戶端失敗cerr << "Accept Socket Error: " << GetLastError() << endl;system("pause");return -1;}// 創(chuàng)建用來和套接字關(guān)聯(lián)的單句柄數(shù)據(jù)信息結(jié)構(gòu)PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA)); // 在堆中為這個(gè)PerHandleData申請(qǐng)指定大小的內(nèi)存PerHandleData -> socket = acceptSocket;memcpy (&PerHandleData -> ClientAddr, &saRemote, RemoteLen);clientGroup.push_back(PerHandleData); // 將單個(gè)客戶端數(shù)據(jù)指針放到客戶端組中// 將接受套接字和完成端口關(guān)聯(lián)CreateIoCompletionPort((HANDLE)(PerHandleData -> socket), completionPort, (DWORD)PerHandleData, 0);// 開始在接受套接字上處理I/O使用重疊I/O機(jī)制// 在新建的套接字上投遞一個(gè)或多個(gè)異步// WSARecv或WSASend請(qǐng)求,這些I/O請(qǐng)求完成后,工作者線程會(huì)為I/O請(qǐng)求提供服務(wù) // 單I/O操作數(shù)據(jù)(I/O重疊)LPPER_IO_OPERATION_DATA PerIoData = NULL;PerIoData = (LPPER_IO_OPERATION_DATA)GlobalAlloc(GPTR, sizeof(PER_IO_OPERATEION_DATA));ZeroMemory(&(PerIoData -> overlapped), sizeof(OVERLAPPED));PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;PerIoData->operationType = 0; // readDWORD RecvBytes;DWORD Flags = 0;WSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);}system("pause");return 0; }// 開始服務(wù)工作線程函數(shù) DWORD WINAPI ServerWorkThread(LPVOID IpParam) {HANDLE CompletionPort = (HANDLE)IpParam;DWORD BytesTransferred;LPOVERLAPPED IpOverlapped;LPPER_HANDLE_DATA PerHandleData = NULL;LPPER_IO_DATA PerIoData = NULL;DWORD RecvBytes;DWORD Flags = 0;BOOL bRet = false;while(true){bRet = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&IpOverlapped, INFINITE);if(bRet == 0){cerr << "GetQueuedCompletionStatus Error: " << GetLastError() << endl;return -1;}PerIoData = (LPPER_IO_DATA)CONTAINING_RECORD(IpOverlapped, PER_IO_DATA, overlapped);// 檢查在套接字上是否有錯(cuò)誤發(fā)生if(0 == BytesTransferred){closesocket(PerHandleData->socket);GlobalFree(PerHandleData);GlobalFree(PerIoData);continue;}// 開始數(shù)據(jù)處理,接收來自客戶端的數(shù)據(jù)WaitForSingleObject(hMutex,INFINITE);cout << "A Client says: " << PerIoData->databuff.buf << endl;ReleaseMutex(hMutex);// 為下一個(gè)重疊調(diào)用建立單I/O操作數(shù)據(jù)ZeroMemory(&(PerIoData->overlapped), sizeof(OVERLAPPED)); // 清空內(nèi)存PerIoData->databuff.len = 1024;PerIoData->databuff.buf = PerIoData->buffer;PerIoData->operationType = 0; // readWSARecv(PerHandleData->socket, &(PerIoData->databuff), 1, &RecvBytes, &Flags, &(PerIoData->overlapped), NULL);}return 0; }// 發(fā)送信息的線程執(zhí)行函數(shù) DWORD WINAPI ServerSendThread(LPVOID IpParam) {while(1){char talk[200];gets(talk);int len;for (len = 0; talk[len] != '\0'; ++len){// 找出這個(gè)字符組的長(zhǎng)度}talk[len] = '\n';talk[++len] = '\0';printf("I Say:");cout << talk;WaitForSingleObject(hMutex,INFINITE);for(int i = 0; i < clientGroup.size(); ++i){send(clientGroup[i]->socket, talk, 200, 0); // 發(fā)送信息}ReleaseMutex(hMutex); }return 0; }


?

// IOCP_TCPIP_Socket_Client.cpp#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <winsock2.h> #include <Windows.h>using namespace std;#pragma comment(lib, "Ws2_32.lib") // Socket編程需用的動(dòng)態(tài)鏈接庫(kù)SOCKET sockClient; // 連接成功后的套接字 HANDLE bufferMutex; // 令其能互斥成功正常通信的信號(hào)量句柄 const int DefaultPort = 6000;int main() { // 加載socket動(dòng)態(tài)鏈接庫(kù)(dll)WORD wVersionRequested;WSADATA wsaData; // 這結(jié)構(gòu)是用于接收Wjndows Socket的結(jié)構(gòu)信息的wVersionRequested = MAKEWORD( 2, 2 ); // 請(qǐng)求2.2版本的WinSock庫(kù)int err = WSAStartup( wVersionRequested, &wsaData );if ( err != 0 ) { // 返回值為零的時(shí)候是表示成功申請(qǐng)WSAStartupreturn -1;}if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { // 檢查版本號(hào)是否正確WSACleanup( );return -1; }// 創(chuàng)建socket操作,建立流式套接字,返回套接字號(hào)sockClientsockClient = socket(AF_INET, SOCK_STREAM, 0);if(sockClient == INVALID_SOCKET) { printf("Error at socket():%ld\n", WSAGetLastError()); WSACleanup(); return -1; } // 將套接字sockClient與遠(yuǎn)程主機(jī)相連// int connect( SOCKET s, const struct sockaddr* name, int namelen);// 第一個(gè)參數(shù):需要進(jìn)行連接操作的套接字// 第二個(gè)參數(shù):設(shè)定所需要連接的地址信息// 第三個(gè)參數(shù):地址的長(zhǎng)度SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本地回路地址是127.0.0.1; addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(DefaultPort);while(SOCKET_ERROR == connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR))){// 如果還沒連接上服務(wù)器則要求重連cout << "服務(wù)器連接失敗,是否重新連接?(Y/N):";char choice;while(cin >> choice && (!((choice != 'Y' && choice == 'N') || (choice == 'Y' && choice != 'N')))){cout << "輸入錯(cuò)誤,請(qǐng)重新輸入:";cin.sync();cin.clear();}if (choice == 'Y'){continue;}else{cout << "退出系統(tǒng)中...";system("pause");return 0;}}cin.sync();cout << "本客戶端已準(zhǔn)備就緒,用戶可直接輸入文字向服務(wù)器反饋信息。\n";send(sockClient, "\nAttention: A Client has enter...\n", 200, 0);bufferMutex = CreateSemaphore(NULL, 1, 1, NULL); DWORD WINAPI SendMessageThread(LPVOID IpParameter);DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter);HANDLE sendThread = CreateThread(NULL, 0, SendMessageThread, NULL, 0, NULL); HANDLE receiveThread = CreateThread(NULL, 0, ReceiveMessageThread, NULL, 0, NULL); WaitForSingleObject(sendThread, INFINITE); // 等待線程結(jié)束closesocket(sockClient);CloseHandle(sendThread);CloseHandle(receiveThread);CloseHandle(bufferMutex);WSACleanup(); // 終止對(duì)套接字庫(kù)的使用printf("End linking...\n");printf("\n");system("pause");return 0; }DWORD WINAPI SendMessageThread(LPVOID IpParameter) {while(1){string talk;getline(cin, talk);WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被占用) if("quit" == talk){talk.push_back('\0');send(sockClient, talk.c_str(), 200, 0);break;}else{talk.append("\n");}printf("\nI Say:(\"quit\"to exit):");cout << talk;send(sockClient, talk.c_str(), 200, 0); // 發(fā)送信息ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源占用完畢) }return 0; }DWORD WINAPI ReceiveMessageThread(LPVOID IpParameter) {while(1){ char recvBuf[300];recv(sockClient, recvBuf, 200, 0);WaitForSingleObject(bufferMutex, INFINITE); // P(資源未被占用) printf("%s Says: %s", "Server", recvBuf); // 接收信息ReleaseSemaphore(bufferMutex, 1, NULL); // V(資源占用完畢) }return 0; }

新人創(chuàng)作打卡挑戰(zhàn)賽發(fā)博客就能抽獎(jiǎng)!定制產(chǎn)品紅包拿不停!

總結(jié)

以上是生活随笔為你收集整理的windows IOCP模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。