聊天室-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. 如果失败就将自己从客户端容器中删除

#include <stdio.h>

// 1. 包含必要的头文件和库, 必须位于 windows之前
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") #include <windows.h>
#include <ws2tcpip.h> // 动态数组
#include <vector>
using namespace std; // 用于保存所有连接的客户端
vector<SOCKET> ClientList; // 创建一个自定义的 OVERLAPPED 的结构,附加一些数据
struct MyOverLapped
{
// 保留原始的重叠结构
OVERLAPPED Overlapped; // 自己添加新的数据
WSABUF WsaBuf;
}; // 工具函数,用于判断是否执行成功
VOID CheckResult(BOOL Value, LPCWSTR ErrMsg)
{
// 如果 Value 非空,就表示执行成功
if (Value == FALSE)
{
printf("ErrMsg: %ls\n", ErrMsg);
system("pause"); ExitProcess();
}
} // 用于执行接收数据的线程
DWORD WINAPI ThreadRoutine(LPVOID lpThreadParameter)
{
// 保存函数的执行结果
BOOL Result = FALSE; // 保存实际的操作数量
DWORD RealOper = ; // 定义完成键,作用是区分IO请求是谁发送的,在绑定的时候设置
ULONG_PTR CompletKey = ; // 重叠 IO 结构
MyOverLapped* pOverLapped = nullptr; // 获取 IOCP 内核对象
HANDLE IoHandle = (HANDLE)lpThreadParameter; // 2. 在循环中不断的接收数据,如果返回值为 > 0 表示成功
while (TRUE)
{
// 当有 IO 请求完成时,从中获取完成的信息,没有就休眠
Result = GetQueuedCompletionStatus(
IoHandle, // IOCP 内核对象
&RealOper, // 实际操作数量
&CompletKey, // 完成键
(LPOVERLAPPED*)& pOverLapped, // 存放重叠结构
INFINITE); // 等待时长 // 3. 将当前的套接字关闭并从列表移除
if (Result == FALSE && RealOper == )
{
for (int i = ; i < ClientList.size(); ++i)
{
// 找到当前对应的套接字
if (ClientList[i] == (SOCKET)CompletKey)
{
// 收尾工作
closesocket((SOCKET)CompletKey);
printf("%08X 退出了聊天室\n", (SOCKET)CompletKey);
ClientList.erase(ClientList.begin() + i);
break;
}
}
}
else
{
// 转发给当前在线的除自己以外的所有客户端
for (size_t i = ; i < ClientList.size(); ++i)
{
// 排除掉自己,不会发消息给自己
if (ClientList[i] == (SOCKET)CompletKey)
continue; // 直接转发消息
send(ClientList[i], pOverLapped->WsaBuf.buf, pOverLapped->WsaBuf.len, );
} // 释放堆空间的数据
delete[] pOverLapped->WsaBuf.buf;
delete pOverLapped; // 每一个异步 IO 操作都需要重叠结构
MyOverLapped* OverLapped = new MyOverLapped{ };
OverLapped->WsaBuf.len = 0x100;
OverLapped->WsaBuf.buf = new char[0x100]{ }; // 先投递一个接收的消息
DWORD RealRecv = , Flags = ;
WSARecv(
(SOCKET)CompletKey, // 客户端套接字
&OverLapped->WsaBuf, // 保存缓冲区和缓冲区的大小
, // 缓冲区的个数
&RealRecv, // 实际操作数
&Flags, // 标志
(OVERLAPPED*)OverLapped,// 重叠结构
NULL); // 回调函数
}
} return ;
} int main()
{
// [1] 创建一个完成端口内核对象,固定写法
HANDLE IoHanlde = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, NULL, , ); // [2] 获取当前 CPU 的数量
SYSTEM_INFO SystemInfo = { };
GetSystemInfo(&SystemInfo); // [3] 根据获取的 CPU 数量,创建线程
for (int i = ; i < SystemInfo.dwNumberOfProcessors; ++i)
CreateThread(NULL, NULL, ThreadRoutine, (LPVOID)IoHanlde, NULL, NULL); // 2. 初始化网络环境并判断是否成功[ 搜索信号(2G?3G?4G?) ]
WSAData WsaData = { };
if (!WSAStartup(MAKEWORD(, ), &WsaData))
CheckResult(WsaData.wVersion == 0x0202, L"初始化网络环境失败"); // 3. 创建套接字(IP+PORT) [ 买手机 ]
SOCKET ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
CheckResult(ServerSocket != INVALID_SOCKET, L"套接字创建失败"); // 4. 绑定套接字,提供IP和端口 (办手机卡)
sockaddr_in ServerAddr = { };
ServerAddr.sin_port = htons(0x1515); // 端口
ServerAddr.sin_family = AF_INET; // 协议类型
inet_pton(AF_INET, "127.0.0.1", &ServerAddr.sin_addr.S_un);
BOOL Result = bind(ServerSocket, // 要绑定的套接字
(SOCKADDR*)& ServerAddr, // 服务器的地址和IP对应的结构体
sizeof(sockaddr_in)); // 必须要指定大小
CheckResult(Result != SOCKET_ERROR, L"套接字绑定失败"); // 5. 监听套接字 (开机,等待连接)
// - 监听谁,最多等待多少个客户端的链接
Result = listen(ServerSocket, SOMAXCONN); // 6. 循环等待客户端的连接(接电话)
while (true)
{
// 接收客户端
int dwSize = sizeof(sockaddr_in);
sockaddr_in ClientAddr = { }; // 接收的客户端ip和端口
SOCKET ClientSocket = accept(ServerSocket,
(SOCKADDR*)&ClientAddr, &dwSize); // 添加到容器中
printf("%08X 进入了聊天室\n", ClientSocket);
ClientList.push_back(ClientSocket); // [4] 绑定套接字对象到 IOCP 句柄
CreateIoCompletionPort((HANDLE)ClientSocket,
// (完成键)会在接收到消息时,传入到线程函数中
IoHanlde, (ULONG_PTR)ClientSocket, ); // [5] 每一个异步 IO 操作都需要重叠结构
MyOverLapped* OverLapped = new MyOverLapped{ };
OverLapped->WsaBuf.len = 0x100;
OverLapped->WsaBuf.buf = new char[0x100]{ }; // [6] 先投递一个接收的消息
DWORD RealRecv = , Flags = ;
WSARecv(
ClientSocket, // 客户端套接字
&OverLapped->WsaBuf, // 保存缓冲区和缓冲区的大小
, // 缓冲区的个数
&RealRecv, // 实际操作数
&Flags, // 标志
(OVERLAPPED*)OverLapped,// 重叠结构
NULL); // 回调函数
} // 7. 关闭套接字执行清理工作
closesocket(ServerSocket); // 8. 清理网络环境
WSACleanup(); system("pause");
return ;
}

网络基础编程_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. mac系统下配置aapt环境变量

    在当前用户目录下新建.bash_profile文件,执行以下命令: vi .bash_profile 然后保存,输入命令  :wq 最后使命令生效,执行命令 source .bash_profile

  2. 如何将Eclipse中的项目迁移到Android Studio中

    如果你之前有用Eclipse做过安卓开发,现在想要把Eclipse中的项目导入到Android Studio的环境中,那么首先要做的是生成Build Gradle的文件.因为Android Studi ...

  3. 【转】Commonjs规范及Node模块实现

    前言: Node在实现中并非完全按照CommonJS规范实现,而是对模块规范进行了一定的取舍,同时也增加了少许自身需要的特性.本文将详细介绍NodeJS的模块实现 引入 nodejs是区别于javas ...

  4. hdu2089(数位DP 递推形式)

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. bzoj3389

    3389: [Usaco2004 Dec]Cleaning Shifts安排值班 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 367  Solved: ...

  6. asp.net Identity2 角色(Role)的使用(一)初始配置

    asp.net Identity自带有角色功能,但默认的模板并没有启用.启用Role的步骤如下:定义role模型--配置角色管理器---配置初始化器---修改数据库上下对象---在应用程序启动文件中配 ...

  7. jquery cloudzoom 3.0,magiczoom 放大镜插件 破解 移除版权信息

    jquery Cloud Zoom一款放大镜插件.但是无奈 官方下载的始终有版权信息,因此想到如下方法去掉版权信息,测试可行! 官方网址:http://www.starplugins.com/clou ...

  8. E20180318-hm

    Scalability 可扩展性 scale  n. 规模; 比例(尺); 鱼鳞; 级别; vt. 测量; 攀登; 刮去…的鳞片; vi. 衡量; 攀登; (鳞屑) 脱落; 生水垢; collabor ...

  9. UVA1437 String painter

    传送门 我们先考虑从一个空白串变成\(B\),这样的话用区间dp,区间dp,设\(f[l][r]\)表示区间\((l,r)\)的最小次数,当\(l==r\)时为\(1\),当\(s[l]==s[r]\ ...

  10. docker速记

    1.docker:一个轻量级的虚拟机.是一个容器 2.Linux系统包括—RedHat(商业版).Centos.Ubuntu 3.docker比作码头的集装箱,image镜像就是基石,images类似 ...