网络基础编程_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 ...
随机推荐
- java 内存简介
java程序对内存分配的方式一般有三种: (1) 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量. (2) 在栈上创建. 在执行函数时,函数内局 ...
- mysql15--垂直分表水平分表
分表技术(表的结构不能变) 分表技术有(水平分割和垂直分割) 当一张越来越大时候,即使添加索引还慢的话,我们可以使用分表 以qq用户表来具体的说明一下分表的操作. 思路如图 : 首先我创建三张表 u ...
- mysql06---权限控制
mysql权限管理: mysql的权限控制,首先在user表判断有没有权限连,连上后看有没有全局权限.然后看db表有哪些库级别的权限.然后看tables_priv表有哪些表级别的权限.最后还可以看有哪 ...
- ubuntu12.04安装tftp,配置,修改目录,错误类型
[前言]学习嵌入式,需要配置tftp服务,在网上搜了搜,很多,但是配置了,我的老是出现Error code 1: File not found错误,经过探索和一个大哥的博客http://blog.cs ...
- DOM方式解析xml实例2
老样子,javabean实体类: import java.io.*; public class Book implements Serializable { private int id; priva ...
- mac多线程下载神器
本文参考:https://blog.csdn.net/orangleliu/article/details/46834429 神器:axel 安装(已经安装homebrew前提下,没有请参考:http ...
- I.MX6 su.c 测试
/************************************************************************* * I.MX6 su.c 测试 * 说明: * 今 ...
- 并不对劲的noip2017d1t3
因为A掉了d1t1,十分开心,把d1t3的代码调出来了. 一般情况下,noip每一天总有一道dp题,然而d1前两道题都不是,再看看第三题的数据范围,就能大概猜出是dp了. 这道题和最短路计数看上去很像 ...
- bzoj 3991 寻宝游戏
题目大意: 一颗树 有一个点的集合 对于每个集合的答案为 从集合内一个点遍历集合内所有点再返回的距离最小值 每次可以选择一个点 若在集合外便加入集合 若在集合内就删除 求每次操作后这个集合的答案 思路 ...
- [转]python的startswith()方法
描述 Python startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False.如果参数 beg 和 end 指定值,则在指定范围内检查. 语法 ...