网络基础编程_5.4聊天室-IOCP服务器
聊天室-IOCP服务器
main
创建完成端口内核对象(CreateIoCompletionPort)
获取核心数并创建线程(GetSystemInfo + CreateThread)
创建套接字并绑定接听(socket + bind + listen)
接收客户端并绑定IOCP(accept + CreateIoCompletionPort)
将客户端套接字添加到队列(vector.pushback)
投递一个客户端套接字的IO请求(WSARecv)
thread
从完成队列中获取完成的消息(GetQueuedCompletionStatus)
判断是否获取成功,如果成功就转发消息,投递一个新的IO请求
如果失败就将自己从客户端容器中删除
#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服务器的更多相关文章
- 网络编程TCP协议-聊天室
网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...
- Linux应用程序设计之网络基础编程
1.TCP/IP协议概述 1.1.OSI参考模型及TCP/IP参考模型 OSI协议参考模型是基于国际标准化组织(ISO)的建议发展起来的,从上到下工分为7层:应用层,表示层,会话层,传输层,网络层,数 ...
- 网络编程-基于Websocket聊天室(IM)系统
目录 一.HTML5 - Websocket协议 二.聊天室(IM)系统的设计 2.1.使用者眼中的聊天系统 2.2.开发者眼中的聊天系统 2.3.IM系统的特性 2.4.心跳机制:解决网络的不确定性 ...
- 使用Android网络编程实现简易聊天室
在Java中我们可以利用socket编程实现聊天室,在Android中也一样,因为Android完全支持JDK本身的TCP.UDP网络通信API.我们可以使用ServerSocket.Socket来建 ...
- Java利用TCP编程实现简单聊天室
前言: 本文是我在学习尚学堂JAVA300集第二季网络编程部分仿照视频内容实现而成 具体可以去尚学堂官网观看视频学习 一.实现思路 实现聊天室的最核心部分就是JAVA的TCP网络编程. TCP 传输控 ...
- 手动搭建I/O网络通信框架4:AIO编程模型,聊天室终极改造
第一章:手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊 第二章:手动搭建I/O网络通信框架2:BIO编程模型实现群聊 第三章:手动搭建I/O网络通信框架3:NI ...
- Linux C 网络编程——多线程的聊天室实现(server端)
server端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人.可进行更改),每一个人所发送的消息其它用户均能够收到.用户能够任意的增加或退出(推出以字符串"bye"实 ...
- Linux C 网络编程——多线程的聊天室实现(服务器端)
服务器端的主要功能: 实现多用户群体聊天功能(此程序最多设定为10人,可进行更改),每个人所发送的消息其他用户均可以收到.用户可以随意的加入或退出(推出以字符串"bye"实现),服 ...
- 【网络基础编程】第三节 C/S
学习地址: C语言中文网 - 实现迭代服务端和客户端 GNU - Closing a Socket 前面介绍的程序,不管Service 端还是 Client端,都有一个问题,就是处理完一个 accep ...
随机推荐
- Hadoop Hive概念学习系列之hive里的索引(十三)
Hive支持索引,但是Hive的索引与关系型数据库中的索引并不相同,比如,Hive不支持主键或者外键. Hive索引可以建立在表中的某些列上,以提升一些操作的效率,例如减少MapReduce任务中需要 ...
- URAL1099 Work Scheduling —— 一般图匹配带花树
题目链接:https://vjudge.net/problem/URAL-1099 1099. Work Scheduling Time limit: 0.5 secondMemory limit: ...
- bzoj 3105: [cqoi2013]新Nim游戏【线性基+贪心】
nim游戏的先手必胜条件是所有堆的火柴个数异或和为0,也就是找一个剩下火柴堆数没有异或和为0的子集的方案,且这个方案保证剩下的火柴个数总和最大 然后我就不会了,其实我到现在也不知道拟阵是个什么玩意-- ...
- bzoj 2809: [Apio2012]dispatching【dfs序+主席树】
可并堆就可以,但是想复健一下主席树. 考虑枚举管理者,然后选忍者的时候在子树中贪心的从小到大选.做成dfs序就是选区间内和小于等于k的最多点.可以用主席树,查询的时候在主席树上二分即可 这里注意,为了 ...
- bzoj 2508: 简单题【拉格朗日乘数法】
大概是对于f(x,y)求min,先把x看成常数,然后得到关于y的一元二次方程,然后取一元二次极值把y用x表示,再把x作为未知数带回去化简,最后能得到一个一元二次的式子,每次修改这个式子的参数即可. 智 ...
- bzoj 3052: [wc2013]糖果公园【树上带修改莫队】
参考:http://blog.csdn.net/lych_cys/article/details/50845832 把树变成dfs括号序的形式,注意这个是不包含lca的(除非lca是两点中的一个) 然 ...
- bzoj 4517: [Sdoi2016]排列计数【容斥原理+组合数学】
第一个一眼就A的容斥题! 这个显然是容斥的经典问题------错排,首先考虑没有固定的情况,设\( D_n \)为\( n \)个数字的错排方案数. \[ D_n=n!-\sum_{t=1}^{n}( ...
- 微信小程序中如何使用setData --- 修改数组对象、修改对象
看代码吧~ 这是修改对象 this.setData({ allStageIndex: e.detail.value, [`projectDetailsData.stage`]: this.data.a ...
- java String类为什么是final的
1.为了安全 java 必须借助操作系统本身的力量才能做事,jdk提供的很多核心类比如String,这类内的很多方法 都不是java编程语言本身编写的,很多方法都是调用操作系统本地的api,如果被继承 ...
- [转]linux之date命令MYSQL用户管理
转自:http://www.cnblogs.com/hencehong/archive/2013/03/19/mysql_user.html 一. 用户登录 格式: mysql -h主机 ...