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