文章目录

  • 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)相关推荐

  1. 网络编程项目(聊天室项目)

    一.实现目标 一个在Linux下可以使用的聊天软件,要求至少实现如下功能: 1. 采用Client/Server架构 2. Client A 登陆聊天服务器前,需要注册自己的ID和密码 3. 注册成功 ...

  2. 网络编程--TCP-qq聊天室

    QQ聊天服务器端: # 开发者:Virtuous # 开发版本:1.0 # 开发时间: 2022/9/21 13:05from socket import *s=socket(AF_INET,SOCK ...

  3. C#网络编程 - 局域网聊天室(UDP)

    一:知识预览 TextBox禁止编辑 ComboBox元素的添加 获取本机网卡IPs 多道程序设计(多线程) 二:界面设计 三:通讯逻辑 [知识预览] 1> TextBox禁止编辑 privat ...

  4. Python实现网络多人聊天室

    网络多人聊天室 相关连接:Python实现网络图形化界面多人聊天室 文件结构: chatroom ├── client.py  # 客户端代码 ├── language.py  # 语言文件 ├── ...

  5. java全双工_java网络编程TCP聊天全双工

    我正在实现服务器和客户端之间的简单TCP聊天.我使用多线程,因此服务器和客户端可以同时发送和接收数据(全双工).该程序可以工作,但如果服务器有一个控制台既可以输入发送消息,也可以显示接收消息(对于客户 ...

  6. java 编程原理_Java网络编程 -- 网络编程基础原理

    Hello,今天记录下 Java网络编程 --> 网络编程基础原理. 一起学习,一起进步.继续沉淀,慢慢强大.希望这文章对您有帮助.若有写的不好的地方,欢迎评论给建议哈! 初写博客不久,我是杨展 ...

  7. 深入分析websocket协议,从3个方面设计网络应用层协议丨网络编程|网络IO|epoll|socket|网络协议丨c/c++linux服务器开发

    深入分析websocket协议,从3个方面设计网络应用层协议 视频讲解如下: 深入分析websocket协议,从3个方面设计网络应用层协议丨网络编程|网络IO|epoll|socket|网络协议丨c/ ...

  8. epoll原理剖析以及reactor模型应用丨网络编程|网络IO|select|poll|socket|reactor多核实现丨c/c++linux服务器开发

    epoll原理剖析以及reactor模型应用 视频讲解如下,点击观看: epoll原理剖析以及reactor模型应用丨网络编程|网络IO|select|poll|socket|reactor多核实现丨 ...

  9. Java网络编程(网络基础(IP端口号网络通信协议)、TCP编程、UDP编程和URL编程原理以及常用方法的实例)

    网络编程 网络基础概述 计算机网络:   把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大.功能强的网络系统,从而使众多的计算机可以方便地互相传递信息.共享硬件.软件.数据信息等 ...

最新文章

  1. SmartAuditor----IT访问审计解决方案
  2. java blockingqueue_Java多线程进阶(三一)—— J.U.C之collections框架:BlockingQueue接口...
  3. Java当中的HashSet
  4. 【TensorFlow学习笔记:神经网络优化(6讲)】
  5. 小技巧来助阵 玩转Chrome浏览器
  6. 静态成员调用java,Java 反射 静态变量 静态方法 静态成员 调用 获取修饰符 判断是否为静态...
  7. m3u8格式转换mp4软件_怎么把mkv格式转换成mp4?教你转换mkv格式的方法
  8. 仿站小工具和小飞兔(扒取网站页面)
  9. 计算机桌面黑底怎么弄,win7怎么设置桌面背景 win7桌面背景变成黑色问题
  10. liunx命令大全建议粘贴到word文档可方便查询
  11. JavaScript广告图片跟随滚动
  12. AD19 基础应用技巧(快速定义PCB板框,CAD中DWG转DXF格式导入)
  13. 前端技术(7) : 省市区联动并设置默认值II
  14. 常用的MATLAB网络资源
  15. windows 和 Linux 查看IP属性(ipconfig,ifconfig)
  16. 系分 - 计算机组成与体系结构
  17. 了解中国的组织结构后续
  18. 【前端知识之JS】BOM的理解
  19. c# 小票打印机打条形码_C#条形码的生成与打印
  20. using NPOI.XSSF.UserModel 报错

热门文章

  1. mac系统重置Mysql密码
  2. 网站搭建需要什么技术?
  3. vue框架如何将SPA项目改为SSR项目
  4. 农行网上银行交电费痛苦经历
  5. UOS服务器操作系统部署EKL
  6. 织梦任意前台密码修改漏洞复现
  7. 【报告分享】2021国民健康洞察报告-丁香(附下载)
  8. kotlin实现的简单个人账户管理APP(三) 自定义View仿支付宝的密码输入框/密码相关逻辑
  9. 中考落幕|教育部:力争到2022年全面实行美育中考,美育到底考什么?
  10. 使用车辆座椅上的压电传感器无创检测呼吸和心率