聊天室-IOCP服务器

main

  1. 创建完成端口内核对象(CreateIoCompletionPort)

  2. 获取核心数并创建线程(GetSystemInfo + CreateThread)

  3. 创建套接字并绑定接听(socket + bind + listen)

  4. 接收客户端并绑定IOCP(accept + CreateIoCompletionPort)

  5. 将客户端套接字添加到队列(vector.pushback)

  6. 投递一个客户端套接字的IO请求(WSARecv)

thread

  1. 从完成队列中获取完成的消息(GetQueuedCompletionStatus)

  2. 判断是否获取成功,如果成功就转发消息,投递一个新的IO请求

  3. 如果失败就将自己从客户端容器中删除

  1. #include <stdio.h>
  2.  
  3. // 1. 包含必要的头文件和库, 必须位于 windows之前
  4. #include <WinSock2.h>
  5. #pragma comment(lib, "ws2_32.lib")
  6.  
  7. #include <windows.h>
  8. #include <ws2tcpip.h>
  9.  
  10. // 动态数组
  11. #include <vector>
  12. using namespace std;
  13.  
  14. // 用于保存所有连接的客户端
  15. vector<SOCKET> ClientList;
  16.  
  17. // 创建一个自定义的 OVERLAPPED 的结构,附加一些数据
  18. struct MyOverLapped
  19. {
  20. // 保留原始的重叠结构
  21. OVERLAPPED Overlapped;
  22.  
  23. // 自己添加新的数据
  24. WSABUF WsaBuf;
  25. };
  26.  
  27. // 工具函数,用于判断是否执行成功
  28. VOID CheckResult(BOOL Value, LPCWSTR ErrMsg)
  29. {
  30. // 如果 Value 非空,就表示执行成功
  31. if (Value == FALSE)
  32. {
  33. printf("ErrMsg: %ls\n", ErrMsg);
  34. system("pause"); ExitProcess();
  35. }
  36. }
  37.  
  38. // 用于执行接收数据的线程
  39. DWORD WINAPI ThreadRoutine(LPVOID lpThreadParameter)
  40. {
  41. // 保存函数的执行结果
  42. BOOL Result = FALSE;
  43.  
  44. // 保存实际的操作数量
  45. DWORD RealOper = ;
  46.  
  47. // 定义完成键,作用是区分IO请求是谁发送的,在绑定的时候设置
  48. ULONG_PTR CompletKey = ;
  49.  
  50. // 重叠 IO 结构
  51. MyOverLapped* pOverLapped = nullptr;
  52.  
  53. // 获取 IOCP 内核对象
  54. HANDLE IoHandle = (HANDLE)lpThreadParameter;
  55.  
  56. // 2. 在循环中不断的接收数据,如果返回值为 > 0 表示成功
  57. while (TRUE)
  58. {
  59. // 当有 IO 请求完成时,从中获取完成的信息,没有就休眠
  60. Result = GetQueuedCompletionStatus(
  61. IoHandle, // IOCP 内核对象
  62. &RealOper, // 实际操作数量
  63. &CompletKey, // 完成键
  64. (LPOVERLAPPED*)& pOverLapped, // 存放重叠结构
  65. INFINITE); // 等待时长
  66.  
  67. // 3. 将当前的套接字关闭并从列表移除
  68. if (Result == FALSE && RealOper == )
  69. {
  70. for (int i = ; i < ClientList.size(); ++i)
  71. {
  72. // 找到当前对应的套接字
  73. if (ClientList[i] == (SOCKET)CompletKey)
  74. {
  75. // 收尾工作
  76. closesocket((SOCKET)CompletKey);
  77. printf("%08X 退出了聊天室\n", (SOCKET)CompletKey);
  78. ClientList.erase(ClientList.begin() + i);
  79. break;
  80. }
  81. }
  82. }
  83. else
  84. {
  85. // 转发给当前在线的除自己以外的所有客户端
  86. for (size_t i = ; i < ClientList.size(); ++i)
  87. {
  88. // 排除掉自己,不会发消息给自己
  89. if (ClientList[i] == (SOCKET)CompletKey)
  90. continue;
  91.  
  92. // 直接转发消息
  93. send(ClientList[i], pOverLapped->WsaBuf.buf, pOverLapped->WsaBuf.len, );
  94. }
  95.  
  96. // 释放堆空间的数据
  97. delete[] pOverLapped->WsaBuf.buf;
  98. delete pOverLapped;
  99.  
  100. // 每一个异步 IO 操作都需要重叠结构
  101. MyOverLapped* OverLapped = new MyOverLapped{ };
  102. OverLapped->WsaBuf.len = 0x100;
  103. OverLapped->WsaBuf.buf = new char[0x100]{ };
  104.  
  105. // 先投递一个接收的消息
  106. DWORD RealRecv = , Flags = ;
  107. WSARecv(
  108. (SOCKET)CompletKey, // 客户端套接字
  109. &OverLapped->WsaBuf, // 保存缓冲区和缓冲区的大小
  110. , // 缓冲区的个数
  111. &RealRecv, // 实际操作数
  112. &Flags, // 标志
  113. (OVERLAPPED*)OverLapped,// 重叠结构
  114. NULL); // 回调函数
  115. }
  116. }
  117.  
  118. return ;
  119. }
  120.  
  121. int main()
  122. {
  123. // [1] 创建一个完成端口内核对象,固定写法
  124. HANDLE IoHanlde = CreateIoCompletionPort(
  125. INVALID_HANDLE_VALUE, NULL, , );
  126.  
  127. // [2] 获取当前 CPU 的数量
  128. SYSTEM_INFO SystemInfo = { };
  129. GetSystemInfo(&SystemInfo);
  130.  
  131. // [3] 根据获取的 CPU 数量,创建线程
  132. for (int i = ; i < SystemInfo.dwNumberOfProcessors; ++i)
  133. CreateThread(NULL, NULL, ThreadRoutine, (LPVOID)IoHanlde, NULL, NULL);
  134.  
  135. // 2. 初始化网络环境并判断是否成功[ 搜索信号(2G?3G?4G?) ]
  136. WSAData WsaData = { };
  137. if (!WSAStartup(MAKEWORD(, ), &WsaData))
  138. CheckResult(WsaData.wVersion == 0x0202, L"初始化网络环境失败");
  139.  
  140. // 3. 创建套接字(IP+PORT) [ 买手机 ]
  141. SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  142. CheckResult(ServerSocket != INVALID_SOCKET, L"套接字创建失败");
  143.  
  144. // 4. 绑定套接字,提供IP和端口 (办手机卡)
  145. sockaddr_in ServerAddr = { };
  146. ServerAddr.sin_port = htons(0x1515); // 端口
  147. ServerAddr.sin_family = AF_INET; // 协议类型
  148. inet_pton(AF_INET, "127.0.0.1", &ServerAddr.sin_addr.S_un);
  149. BOOL Result = bind(ServerSocket, // 要绑定的套接字
  150. (SOCKADDR*)& ServerAddr, // 服务器的地址和IP对应的结构体
  151. sizeof(sockaddr_in)); // 必须要指定大小
  152. CheckResult(Result != SOCKET_ERROR, L"套接字绑定失败");
  153.  
  154. // 5. 监听套接字 (开机,等待连接)
  155. // - 监听谁,最多等待多少个客户端的链接
  156. Result = listen(ServerSocket, SOMAXCONN);
  157.  
  158. // 6. 循环等待客户端的连接(接电话)
  159. while (true)
  160. {
  161. // 接收客户端
  162. int dwSize = sizeof(sockaddr_in);
  163. sockaddr_in ClientAddr = { }; // 接收的客户端ip和端口
  164. SOCKET ClientSocket = accept(ServerSocket,
  165. (SOCKADDR*)&ClientAddr, &dwSize);
  166.  
  167. // 添加到容器中
  168. printf("%08X 进入了聊天室\n", ClientSocket);
  169. ClientList.push_back(ClientSocket);
  170.  
  171. // [4] 绑定套接字对象到 IOCP 句柄
  172. CreateIoCompletionPort((HANDLE)ClientSocket,
  173. // (完成键)会在接收到消息时,传入到线程函数中
  174. IoHanlde, (ULONG_PTR)ClientSocket, );
  175.  
  176. // [5] 每一个异步 IO 操作都需要重叠结构
  177. MyOverLapped* OverLapped = new MyOverLapped{ };
  178. OverLapped->WsaBuf.len = 0x100;
  179. OverLapped->WsaBuf.buf = new char[0x100]{ };
  180.  
  181. // [6] 先投递一个接收的消息
  182. DWORD RealRecv = , Flags = ;
  183. WSARecv(
  184. ClientSocket, // 客户端套接字
  185. &OverLapped->WsaBuf, // 保存缓冲区和缓冲区的大小
  186. , // 缓冲区的个数
  187. &RealRecv, // 实际操作数
  188. &Flags, // 标志
  189. (OVERLAPPED*)OverLapped,// 重叠结构
  190. NULL); // 回调函数
  191. }
  192.  
  193. // 7. 关闭套接字执行清理工作
  194. closesocket(ServerSocket);
  195.  
  196. // 8. 清理网络环境
  197. WSACleanup();
  198.  
  199. system("pause");
  200. return ;
  201. }

网络基础编程_5.4聊天室-IOCP服务器的更多相关文章

  1. 网络编程TCP协议-聊天室

    网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...

  2. Linux应用程序设计之网络基础编程

    1.TCP/IP协议概述 1.1.OSI参考模型及TCP/IP参考模型 OSI协议参考模型是基于国际标准化组织(ISO)的建议发展起来的,从上到下工分为7层:应用层,表示层,会话层,传输层,网络层,数 ...

  3. 网络编程-基于Websocket聊天室(IM)系统

    目录 一.HTML5 - Websocket协议 二.聊天室(IM)系统的设计 2.1.使用者眼中的聊天系统 2.2.开发者眼中的聊天系统 2.3.IM系统的特性 2.4.心跳机制:解决网络的不确定性 ...

  4. 使用Android网络编程实现简易聊天室

    在Java中我们可以利用socket编程实现聊天室,在Android中也一样,因为Android完全支持JDK本身的TCP.UDP网络通信API.我们可以使用ServerSocket.Socket来建 ...

  5. Java利用TCP编程实现简单聊天室

    前言: 本文是我在学习尚学堂JAVA300集第二季网络编程部分仿照视频内容实现而成 具体可以去尚学堂官网观看视频学习 一.实现思路 实现聊天室的最核心部分就是JAVA的TCP网络编程. TCP 传输控 ...

  6. 手动搭建I/O网络通信框架4:AIO编程模型,聊天室终极改造

    第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 第三章:手动搭建I/O网络通信框架3:NI ...

  7. Linux C 网络编程——多线程的聊天室实现(server端)

    server端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人.可进行更改),每一个人所发送的消息其它用户均能够收到.用户能够任意的增加或退出(推出以字符串"bye"实 ...

  8. Linux C 网络编程——多线程的聊天室实现(服务器端)

    服务器端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人,可进行更改),每个人所发送的消息其他用户均可以收到.用户可以随意的加入或退出(推出以字符串"bye"实现),服 ...

  9. 【网络基础编程】第三节 C/S

    学习地址: C语言中文网 - 实现迭代服务端和客户端 GNU - Closing a Socket 前面介绍的程序,不管Service 端还是 Client端,都有一个问题,就是处理完一个 accep ...

随机推荐

  1. Ubuntu 12.10终端Terminal快捷方式调用

    1:使用快捷键:ctrl+alt+t 打开终端 2:在终端上右键,选“Lock to launcher” 这样就锁定在左侧了,需要用时,直接点就打开了.

  2. ubuntu1.8安装lnmp失败

    兴致冲冲的安装好ubuntu1.8. 想安装lnmp,结果失败,失败,失败. 一遍由一遍,很痛苦. 每一遍都要半个小时,甚至更久. 等来的就是失败. 看日志也看不出头绪来. ============= ...

  3. poj-1655 Balancing Act(树的重心+树形dp)

    题目链接: Balancing Act Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11845   Accepted: 4 ...

  4. UITabBarController简单介绍

    一.简单介绍 UITabBarController和UINavigationController类似,UITabBarController也可以轻松地管理多个控制器,轻松完成控制器之间的切换,典型的例 ...

  5. 四:网络--NSURLConnection基本使用

    一.NSURLConnection的常用类 (1)NSURL:请求地址 (2)NSURLRequest:封装一个请求,保存发给服务器的全部数据,包括一个NSURL对象,请求方法.请求头.请求体.... ...

  6. 利用http_load测试Web引擎性能

    http_load是基于linux平台的性能测试工具,它体积非常小,仅100KB.它以并行复用的方式运行,可以测试web服务器的吞吐量与负载. 一.获得http_load httpd_load的官方站 ...

  7. python3中urllib的基本使用

    urllib 在python3中,urllib和urllib2进行了合并,现在只有一个urllib模块,urllib和urllib2的中的内容整合进了urllib.request,urlparse整合 ...

  8. asp.net mvc4 不支持EF6

    参考文章:http://www.cnblogs.com/zeusro/p/3403771.html http://q.cnblogs.com/q/40926/ 解决方法是 1.把EF6删除改用EF5. ...

  9. [知识积累]python3使用xlwt时写入文档字体颜色和边框样式

    可借鉴的网址:https://www.programcreek.com/python/example/39979/xlwt.Alignment 可以直接通过pip安装xlwt 个人理解: xlwt中对 ...

  10. 洛谷 P3625 [APIO2009]采油区域【枚举】

    参考:https://blog.csdn.net/FAreStorm/article/details/49200383 没有技术含量但是难想难写,枚举情况图详见参考blog懒得画了 bzoj蜜汁TTT ...