windows网络编程 --网络聊天室(2)
文章目錄
- IOCP模型
- 常用IOCP函數(shù)
- 基于IOCP的網(wǎng)絡(luò)聊天室
- 服務(wù)器端
- 客戶端
IOCP模型
IOCP :輸入輸出完成端口。
是支持多個(gè)同時(shí)發(fā)生的異步I/O操作的應(yīng)用程序編程接口,IOCP特別適合C/S模式網(wǎng)絡(luò)服務(wù)器端模型。
因?yàn)?#xff0c;讓每一個(gè)socket有一個(gè)線程負(fù)責(zé)同步(阻塞)數(shù)據(jù)處理,one-thread-per-client的缺點(diǎn)是:一是如果連入的客戶多了,就需要同樣多的線程;二是不同的socket的數(shù)據(jù)處理都要線程切換的代價(jià)。
最佳線程數(shù)量=cpu內(nèi)核數(shù)量*2
一個(gè)IOCP對(duì)象,在操作系統(tǒng)中可關(guān)聯(lián)著多個(gè)Socket和(或)文件控制端。 IOCP對(duì)象內(nèi)部有一個(gè)先進(jìn)先出(FIFO)隊(duì)列,用于存放IOCP所關(guān)聯(lián)的輸入輸出端的服務(wù)請(qǐng)求完成消息。請(qǐng)求輸入輸出服務(wù)的進(jìn)程不接收IO服務(wù)完成通知,而是檢查IOCP的消息隊(duì)列以確定IO請(qǐng)求的狀態(tài)。
- 隊(duì)列有消息: (線程池中的)多個(gè)線程負(fù)責(zé)從IOCP消息隊(duì)列中取走完成通知并執(zhí)行數(shù)據(jù)處理;
- 如果隊(duì)列中沒(méi)有消息,那么線程阻塞掛起在該隊(duì)列。這些線程從而實(shí)現(xiàn)了負(fù)載均衡。
IOCP是一個(gè)內(nèi)核對(duì)象,但是他是一個(gè)不需要安全屬性的Windows內(nèi)核對(duì)象
常用IOCP函數(shù)
使用IOCP模型,首先必須創(chuàng)建(或者關(guān)聯(lián))一個(gè)IOCP對(duì)象(與文件句柄):
HANDLE CreateIoCompletionPort([in] HANDLE FileHandle,//要關(guān)聯(lián)的文件句柄[in, optional] HANDLE ExistingCompletionPort,//完成端口句柄[in] ULONG_PTR CompletionKey,//完成鍵[in] DWORD NumberOfConcurrentThreads//允許的最大線程數(shù) );CreateIoCompletionPort 函數(shù)創(chuàng)建 IOCP完成端口并將其與指定的文件句柄相關(guān)聯(lián),或者創(chuàng)建一個(gè)新的未被關(guān)聯(lián)的IOCP對(duì)象。
- 參數(shù)一:接受一個(gè)文件句柄或者為NULL。 文件句柄:將其與一個(gè)已存在的IOCP對(duì)象相關(guān)聯(lián); NULL:創(chuàng)建一個(gè)新的IOCP對(duì)象。
- 參數(shù)二:接受一個(gè)現(xiàn)有IOCP對(duì)象或 NULL 的句柄。現(xiàn)有IOCP對(duì)象: 將其與一個(gè)文件句柄相關(guān)聯(lián);NULL:創(chuàng)建一個(gè)新的IOCP對(duì)象。
- 參數(shù)三:指定文件句柄的每個(gè) I/O 完成數(shù)據(jù)包中包含的每句柄用戶定義完成密鑰。如果是關(guān)聯(lián)已存在文件句柄,則此參數(shù)為此文件句柄,否則為空。
- 參數(shù)四:一個(gè)IOCP允許處理的最大線程數(shù),NULL為系統(tǒng)默認(rèn)。
獲取IOCP的消息隊(duì)列:
BOOL GetQueuedCompletionStatus([in] HANDLE CompletionPort,//IOCP對(duì)象句柄LPDWORD lpNumberOfBytesTransferred,//IO操作傳輸?shù)淖止?jié)數(shù)[out] PULONG_PTR lpCompletionKey,//發(fā)送消息方[out] LPOVERLAPPED *lpOverlapped,//接受消息,并且存儲(chǔ)消息的結(jié)構(gòu)體指針[in] DWORD dwMilliseconds//接受等待時(shí)間 );GetQueuedCompletionStatus 函數(shù)嘗試從指定的 I/O 完成端口取消 I/O 完成數(shù)據(jù)包的排隊(duì)。
如果沒(méi)有排隊(duì)的完成數(shù)據(jù)包,該函數(shù)將等待與完成端口關(guān)聯(lián)的掛起 I/O 操作完成。
說(shuō)白了就是 接收從指定的發(fā)送方發(fā)送的消息,并且取消排隊(duì)。
- 參數(shù)一:正在操作的IOCP對(duì)象。
- 參數(shù)二 :準(zhǔn)備從發(fā)送方接收的數(shù)據(jù)的字節(jié)數(shù)量,接收成功返回接收的字節(jié)數(shù),失敗返回NULL,表示讀取失敗。
- 參數(shù)三:指定的消息發(fā)送方,即客戶端。
- 參數(shù)四:接收消息的一個(gè)結(jié)構(gòu)體,消息信息都存儲(chǔ)在這個(gè)結(jié)構(gòu)體中。
- 參數(shù)五:等待的時(shí)間,相當(dāng)于WaitForSingleoObect的函數(shù)等待。
基于IOCP的網(wǎng)絡(luò)聊天室
服務(wù)器端
#include <WinSock2.h> #include <Windows.h> #include <iostream> #include <ws2tcpip.h> #include <vector> #pragma comment(lib,"ws2_32.lib")std::vector<SOCKET> Vec_Socket;typedef struct My_Overlapped {OVERLAPPED wsaoverlapped;WSABUF wsabuf; }My_Overlapped,*PMy_Overlapped;DWORD WINAPI DoMessage(LPVOID lpParameter ) {HANDLE SocketHandle = (HANDLE)lpParameter;BOOL Result = FALSE;PMy_Overlapped myOverlapped = nullptr;DWORD NumOfReadISze = 0;ULONG_PTR ComplixKey = 0;DWORD Flags = 0;//指向接收與 I / O 操作已完成的文件句柄關(guān)聯(lián)的完成鍵值的變量的指針ULONG_PTR client{ 0 };while (true){//等待消息的讀入Result = GetQueuedCompletionStatus(SocketHandle,&NumOfReadISze,&client, //從每一個(gè)客戶句柄里讀取信息(LPOVERLAPPED*)&myOverlapped,INFINITE);if (Result && NumOfReadISze > 0){for (UINT i = 0; i < Vec_Socket.size(); i++){//發(fā)送消息if (Vec_Socket[i] != client){//句柄不是你自己,往其他人客戶端發(fā)送消息send(Vec_Socket[i], myOverlapped->wsabuf.buf, myOverlapped->wsabuf.len, NULL);}}//清空緩沖區(qū)memset(myOverlapped->wsabuf.buf, 0, 0x100);memset(&myOverlapped->wsaoverlapped, 0, sizeof(OVERLAPPED));WSARecv(client,&myOverlapped->wsabuf,1,&NumOfReadISze,&Flags,(LPWSAOVERLAPPED)myOverlapped,NULL);}else{//卸載客戶端for (UINT i = 0; i < Vec_Socket.size(); i++){if (Vec_Socket[i] == client){closesocket(client);printf("客戶端%u斷開(kāi)了連接\n", Vec_Socket[i]);Vec_Socket.erase(Vec_Socket.begin() + i);}}}}return 1; }int main() {// 創(chuàng)建IOCP對(duì)象HANDLE IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,NULL);if (!IOCP){printf("IOCP創(chuàng)建失敗!\n");return 0;}//獲取CPU的內(nèi)核處理器數(shù)量SYSTEM_INFO system_info{ 0 };GetNativeSystemInfo(&system_info);for (UINT i = 0; i < system_info.dwNumberOfProcessors*2; i++){CreateThread(NULL,NULL,DoMessage,(LPVOID)IOCP, //IOCP傳入NULL,NULL);}//1. 初始化網(wǎng)絡(luò)環(huán)境WSADATA WsaData{ 0 };WSAStartup(MAKEWORD(2, 2), //套接字&WsaData);//2. 創(chuàng)建socket套接字//創(chuàng)建綁定到特定傳輸服務(wù)提供者的套接字。SOCKET serve = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//3. 綁定端口號(hào)IP地址sockaddr_in serverAddr{ 0 };serverAddr.sin_family = AF_INET; //地址族serverAddr.sin_port = htons(666); //端口號(hào)//inet_pton:標(biāo)準(zhǔn)文本呈現(xiàn)形式中將 IPv4 或 IPv6 Internet 網(wǎng)絡(luò)地址轉(zhuǎn)換為其數(shù)字二進(jìn)制形式inet_pton(AF_INET, "172.20.230.126", &serverAddr.sin_addr);//bind:綁定函數(shù)將本地地址與套接字相關(guān)聯(lián)。bind(serve, (sockaddr*)&serverAddr, sizeof(serverAddr));//4. 監(jiān)聽(tīng)//偵聽(tīng)函數(shù)將套接字置于偵聽(tīng)傳入連接的狀態(tài)。listen(serve, SOMAXCONN);sockaddr_in ClientAddr{ 0 };int SockSize = sizeof(ClientAddr);//5. 接收會(huì)話while (1){//連接客戶端與服務(wù)器,返回客戶端句柄SOCKET client = accept(serve, //標(biāo)識(shí)已使用監(jiān)聽(tīng)函數(shù)處于偵聽(tīng)狀態(tài)的套接字(sockaddr*)&ClientAddr,&SockSize);//保存所有的客戶端句柄于一個(gè)容器中Vec_Socket.push_back(client);//將SOCKET關(guān)聯(lián)到IOCPCreateIoCompletionPort((HANDLE)client,IOCP,client,NULL);PMy_Overlapped MyOverlapped = new My_Overlapped{ 0 };MyOverlapped->wsabuf.buf = new char[0x100] {0};MyOverlapped->wsabuf.len = 0x100; //緩沖區(qū)的長(zhǎng)度 DWORD RealSize = 0;DWORD Flags = 0;//接收消息WSARecv(client,&MyOverlapped->wsabuf,1,&RealSize,&Flags,(LPWSAOVERLAPPED)MyOverlapped,NULL);printf("服務(wù)器%u連接到了客戶端\n", client);}closesocket(serve);system("pause");return 0; }客戶端
#include <WinSock2.h> #include <Windows.h> #include <WS2tcpip.h> #include <iostream> #pragma comment(lib,"ws2_32.lib")DWORD WINAPI GetMessageAAA(LPVOID lpParameter ) {SOCKET Socket = (SOCKET)lpParameter;CHAR DstBuff[0x100]{ 0 };while (recv(Socket, DstBuff, 0x100, NULL) > 0){printf("接收:%s\n", DstBuff);}return 1; }int main() {//1. 初始化網(wǎng)絡(luò)連接WSADATA WsaData{ 0 };WSAStartup(MAKEWORD(2, 2), &WsaData);//2. 創(chuàng)建SOCKET套接字SOCKET serve = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//3. 綁定IP地址sockaddr_in serveAddr{ 0 };serveAddr.sin_family = AF_INET; //地址族serveAddr.sin_port = htons(666); //端口號(hào)inet_pton(AF_INET, "172.20.230.126", &serveAddr.sin_addr);//連接服務(wù)器//connect函數(shù)建立與指定套接字的連接。int result = connect(serve, (sockaddr*)&serveAddr, sizeof(serveAddr));if (result != 0) //未發(fā)生錯(cuò)誤,返回零{printf("連接失敗!\n");system("pause");return 0;}//創(chuàng)建線程HANDLE Thread = CreateThread(NULL,NULL,GetMessageAAA,(LPVOID)serve, //傳入當(dāng)前的客戶端句柄NULL,NULL);CHAR lpBuff[0x100]{ 0 };while (scanf("%s", lpBuff) && strcmp(lpBuff, "exit")){send(serve, lpBuff, strlen(lpBuff)+1, NULL);}system("pause");closesocket(serve);return 0; }運(yùn)行效果:
總結(jié)
以上是生活随笔為你收集整理的windows网络编程 --网络聊天室(2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: webrtc视频引擎之video_ren
- 下一篇: win10语言包在c盘哪里,win10系