聊天室-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. UVA - 11488 Hyper Prefix Sets(trie树)

    1.给n个只含0.1的串,求出这些串中前缀的最大和. 例1: 0000 0001 10101 010 结果:6(第1.2串共有000,3+3=6) 例2: 01010010101010101010 1 ...

  2. 部署到Linux并配置Java定时任务

    Java项目部署到Linux并配置定时任务 https://blog.csdn.net/u013850277/article/details/53447391 1.在Eclipse中将程序开发好,并进 ...

  3. saltstack源码-启动1

    决定看salt的源码了.干脆就从最基本的看起来,先看它的启动过程开始第一步用/etc/init.d/salt-master start 启动找到那个文件,发现有3种启动方式,suse,debian,c ...

  4. 摘抄 - 不为人知的JS调用样式的方法---document.createElement().addRule(..)

    很多人可能在调用css样式都是使用传统的方式调用其实有很多方法可以进行调用,如使用内嵌样式,在html直接加入样式,给定外部样式文件,在外部样式文件中使用 @import url(样式文件路径),这些 ...

  5. Struts2自定义返回Json类型result

    本来Struts2有自己的json类型的返回结果,并提供了插件,但是它有一个问题,那就是它会将所有序列化的字段都返回,如果想要制定返回Action的某一个属性,则需要在配置result时,配置参数(这 ...

  6. join示例分析

    join示例分析 public class TestJoin { public static void main(String[] args) throws InterruptedException ...

  7. python 面向对象四 继承和多态

    一.继承 class Animal(object): def run(self): print('Animal is running...') class Dog(Animal): def run(s ...

  8. bzoj 3029: 守卫者的挑战【概率dp】

    以后写dp还是向后转移吧--写的把前面加起来的版本怎么也调不过去 首先注意,因为地图碎片只占1体积,所以>n,<-n的体积是没用的,所以就可以把体积降到n级别,然后用这场胜负像后转移即可, ...

  9. hdu1115【多边形求重心模板】

    1.质量集中在顶点上.n个顶点坐标为(xi,yi),质量为mi,则重心(∑( xi×mi ) / ∑mi, ∑( yi×mi ) / ∑mi) 2.质量分布均匀.这个题就是这一类型,算法和上面的不同. ...

  10. 矩阵快速幂/矩阵加速线性数列 By cellur925

    讲快速幂的时候就提到矩阵快速幂了啊,知道是个好东西,但是因为当时太蒟(现在依然)没听懂.现在把它补上. 一.矩阵快速幂 首先我们来说说矩阵.在计算机中,矩阵通常都是用二维数组来存的.矩阵加减法比较简单 ...