使用IO完成端口实现简单回显服务器
说明
使用IO完成端口实现简单回显服务器,因为是测试用的,所以代码很粗糙。
-
提醒
使用的是ReadFile、WriteFile来实现Overlapped IO,正式场合应该用WSARecv、WSASend,原因:来自《Windows网络编程技术》 8.2.5节
在这里插入图片描述 -
技术点记录下
io以同步方式立马完成时,系统也会将此通知投递到io完成端口通知列表中,这么做的原因是方便用户编码。
SetFileCompletionNotificationModes传入FILE_SKIP_COMPLETION_PORT_ON_SUCCESS告诉系统,io以同步方式立马完成时,不要
将此事件投递到IO完成端口列表中。参看《Windows核心编程》第10章 10.5.4


代码
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <set>
#include <memory>
#include <process.h>#pragma comment(lib, "Ws2_32.lib")class MyOverlapped
{
public:MyOverlapped():m_bIsRead(false){memset(&m_Overlapped, 0, sizeof(OVERLAPPED));}OVERLAPPED m_Overlapped;bool m_bIsRead;
};struct ClientSocketItem
{ClientSocketItem(){hSocket = NULL;memset(szRecv, 0, sizeof(szRecv));nRecvSize = 0;bFinished = false;nWriteOffset = 0;readOverlapped.m_bIsRead = true;writeOverlapped.m_bIsRead = false;}SOCKET hSocket;std::string strIp;MyOverlapped readOverlapped;char szRecv[1024];unsigned int nRecvSize;MyOverlapped writeOverlapped;unsigned int nWriteOffset = 0;bool bFinished;
};
std::set<ClientSocketItem*> g_Clients;HANDLE g_hIoCompletionPort = NULL;bool do_read(ClientSocketItem* pClient)
{if (!pClient){return false;}char c = 0; //测试用,每次只读一个字符DWORD dwReadBytes;if (::ReadFile((HANDLE)(pClient->hSocket), &(pClient->szRecv[pClient->nRecvSize]),1, &dwReadBytes, &(pClient->readOverlapped.m_Overlapped))){return true;}DWORD dwError = ::GetLastError();if (ERROR_IO_PENDING == dwError){return true;}std::cerr << "read failed with error " << dwError << std::endl;return false;
}bool do_write(ClientSocketItem* pClient)
{if (!pClient){return false;}//测试用,每次只发送一个字符DWORD dwWriteBytes = 0;if (::WriteFile((HANDLE)(pClient->hSocket), &(pClient->szRecv[pClient->nWriteOffset]),1, &dwWriteBytes, &(pClient->writeOverlapped.m_Overlapped))){return true;}DWORD dwError = ::GetLastError();if (ERROR_IO_PENDING == dwError){return true;}std::cerr << "write failed with error " << dwError << std::endl;return false;
}bool do_accept(SOCKET hListenSocket)
{sockaddr_in mPeerAddr = { 0 };int nAddrLen = sizeof(sockaddr);SOCKET hClientSocket = accept(hListenSocket, (sockaddr*)(&mPeerAddr), &nAddrLen);if (INVALID_SOCKET == hClientSocket){std::cout << "accept failed with error "<< WSAGetLastError() << std::endl;return false;}else{unsigned long nNoBlock = 0;ioctlsocket(hClientSocket, FIONBIO, &nNoBlock);std::string strIpAddr = inet_ntoa(mPeerAddr.sin_addr);std::cout << "accept success, peer ip is " << strIpAddr.c_str() << std::endl;auto pClient = new ClientSocketItem();pClient->hSocket = hClientSocket;pClient->strIp = strIpAddr;g_Clients.insert(pClient);//附加到IO完成端口上if (g_hIoCompletionPort != ::CreateIoCompletionPort(HANDLE(pClient->hSocket),g_hIoCompletionPort, ULONG_PTR(pClient), 0)){std::cerr << "attach socket to io completion port failed with error"<< ::GetLastError() << std::endl;closesocket(hClientSocket);return false;}//触发读取if (!do_read(pClient)){closesocket(pClient->hSocket);g_Clients.erase(pClient);std::cerr << "do_read failed, close client" << std::endl;}return true;}
}//读写线程函数
unsigned __stdcall ReadWriteThreadFun(void* pParam)
{DWORD dwTransferBytes = 0;ULONG_PTR nCompleteKey = 0;LPOVERLAPPED lpOverlapped = NULL;while (::GetQueuedCompletionStatus(g_hIoCompletionPort, &dwTransferBytes,&nCompleteKey, &lpOverlapped, INFINITE)){if (nCompleteKey == UINT_MAX){std::cout << "user require quit" << std::endl;break;}if (!lpOverlapped){std::cerr << "lpOverlapped is null" << std::endl;break;}ClientSocketItem* pClient = (ClientSocketItem*)nCompleteKey;MyOverlapped* pMyOverlapped = CONTAINING_RECORD(lpOverlapped, MyOverlapped, m_Overlapped);if (!pMyOverlapped || !pClient){std::cerr << "pMyOverlapped or pClient is null" << std::endl;break;}if (pMyOverlapped->m_bIsRead)//read finished notify{char c = pClient->szRecv[pClient->nRecvSize];pClient->nRecvSize += dwTransferBytes;std::cout << "read one char: " << c << std::endl;if (c == '\n'){std::cout << "read finished, start to write" << std::endl;if (!do_write(pClient)){std::cerr << "do_write failed, close socket" << std::endl;closesocket(pClient->hSocket);g_Clients.erase(pClient);}}else{std::cout << "next char read" << std::endl;if (!do_read(pClient)){std::cerr << "do_read failed, close socket" << std::endl;closesocket(pClient->hSocket);g_Clients.erase(pClient);}}}else //write finished notify{char c = pClient->szRecv[pClient->nWriteOffset];std::cout << "send one char: " << c << std::endl;pClient->nWriteOffset += dwTransferBytes;if (pClient->nWriteOffset == pClient->nRecvSize){std::cout << "send finished, close client(" << pClient->strIp.c_str()<< ")" << std::endl;closesocket(pClient->hSocket);g_Clients.erase(pClient);}else{std::cout << "next send" << std::endl;if (!do_write(pClient)){std::cerr << "do_write failed, close socket" << std::endl;closesocket(pClient->hSocket);g_Clients.erase(pClient);}}}}std::cout << "thread quit" << std::endl;return 0;
}int main(int argc, char* argv)
{WORD wVersionRequested = MAKEWORD(2, 2);WSADATA wsaData = { 0 };int err = WSAStartup(wVersionRequested, &wsaData);if (err != 0){return -1;}if (LOBYTE(wsaData.wVersion) != 2 ||HIBYTE(wsaData.wVersion) != 2){WSACleanup();return -1;}SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == hListenSocket){std::cerr << "create socket failed with error " << WSAGetLastError()<< std::endl;return -1;}sockaddr_in mSockAddrIn = { 0 };mSockAddrIn.sin_family = AF_INET;mSockAddrIn.sin_port = htons((u_short)8878);mSockAddrIn.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");if (SOCKET_ERROR == bind(hListenSocket, (sockaddr*)(&mSockAddrIn),sizeof(sockaddr))){std::cerr << "bind failed with error " << WSAGetLastError() << std::endl;return -1;}if (SOCKET_ERROR == listen(hListenSocket, SOMAXCONN)){std::cerr << "listen failed with error " << WSAGetLastError() << std::endl;return -1;}//创建完成端口g_hIoCompletionPort = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);if (NULL == g_hIoCompletionPort){std::cerr << "create io completion port failed with error " << ::GetLastError() << std::endl;return -1;}//创建一堆服务线程for (int i = 0; i < 4; ++i){_beginthreadex(0, 0, ReadWriteThreadFun, 0, 0, nullptr);}while (true){if (!do_accept(hListenSocket)){break;}}::PostQueuedCompletionStatus(g_hIoCompletionPort, 0, UINT_MAX, nullptr);Sleep(2000); return 0;
}
相关文章:
使用IO完成端口实现简单回显服务器
说明 使用IO完成端口实现简单回显服务器,因为是测试用的,所以代码很粗糙。 提醒 使用的是ReadFile、WriteFile来实现Overlapped IO,正式场合应该用WSARecv、WSASend,原因:来自《Windows网络编程技术》 8.2.5节 在这里…...
【ROS】Nav2源码之nav2_behavior_tree详解
1、简介 nav2_bt_navigator实现ROS2节点以行为树的方式来处理。 nav2_behavior_tree模块提供如下功能: 一个c模板类,可以轻松地将ROS2 动作(actions)和服务(services)集成到行为树(Behavior Trees)中。特定于导航的行为树节点。通用的BehaviorTreeEngine类&#…...
SpringBoot---myBatis数据库操作
一,分页查询 现在controller中设置参数,RequestParam(defaultValue "1") 设置默认值 GetMapping public Result page(RequestParam(defaultValue "1") Integer page,RequestParam(defaultValue "10") Integer pageSize…...
力扣541.反转字符串II
原题链接:力扣541.反转字符串II 思路: 其实在遍历字符串的过程中,只要让 i (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。 因为要找的也就是每2 * k 区间的起点,这样写,…...
撕掉Hadoop标签,Cloudera未来可期吗?
Cloudera:大数据的弄潮儿 1、Cloudera发展史2、透过Cloudera看清大数据时代的转变3、参考文章 1、Cloudera发展史 说起Cloudera,就不得不提起Hadoop,Cloudera的过去就是Hadoop时代中的一个缩影。作为全球第一家也是最大一家Hadoop商业化公司&…...
排序算法(1)
这里写目录标题 排序插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序向下调整堆排序 交换排序冒泡排序 排序 插入排序 直接插入排序 直接插入排序是O(N^2)的排序算法 从0下标开始往后排 void InsertSort(int* a,int n)//直接插入排序 {fo…...
Top 5 Cutting-edge technology examples 2023
文章目录 Top 5 Cutting-edge technology examples 20231、Computer Vision2、Natural Language Processing3、Virtual Reality & Augmented Reality4、Deep Machine Learning5、Neuralink Top 5 Cutting-edge technology examples 2023 Cutting-edge technology in 2023 …...
【算法|滑动窗口No.3】leetcode3. 无重复字符的最长子串
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…...
元素的水平居中和垂直几种方案
总结一下各种元素的水平居中和垂直居中方案。 水平居中: 1.行内元素水平居中 text-align: center 定义行内内容(例如文字)如何相对它的块父元素对齐;不仅可以让文字水平居中,还可以让行内元素水平居中 注意:给行内…...
JS和JQuery的区别
JS和jQuery都是用于前端开发的工具,但是它们有一些重要的区别。主要区别如下: JS是一种编程语言,而jQuery是一个JS库。JS可以与其他语言一起使用(如PHP、Python等),而jQuery是JS的一个扩展,只能…...
延时摄影视频制作工具 LRTimelapse mac中文版特点介绍
lrTimelapse mac是一款适用于 Windows 和 macOS 系统的延时摄影视频制作软件,可以帮助用户创建高质量的延时摄影视频。该软件提供了直观的界面和丰富的功能,支持多种时间轴摄影工具和文件格式,并具有高度的可定制性和扩展性。 lrTimelapse ma…...
Mac电脑怎么运行 Office 办公软件
虽然 Office 软件也有 Mac 版本的,但是有蛮多小伙伴用起来还是感觉不得劲,毕竟接触了太久的 Windows,所以想要使用 Windows 版本的 Office 软件。 今天就给大家介绍一下怎么在 Mac 电脑中运行 Windows 版本的办公软件,在这里就需…...
FPGA 如何 固化程序到 FLASH中
1、导出Hardware 2、导出bit文件 3、打开SDK 4、 点击Ok 5、创建工程 6、 输入工程名称:guhua 7、选择 Zynq FSBL 8、单击 guhua、然后点击 build 点击:build all 9、 右键之后,点击:Creat Boot Image 10、点击 Cr…...
电源管理(PMIC)MAX20428ATIA/VY、MAX20428ATIC/VY、MAX20428ATIE/VY适合汽车ADAS应用的开关稳压器
一、概述 MAX20428是一款高效率、八路输出、低压PMIC。OUT1将输入电源升压至5V,电流高达500mA,而三个同步降压转换器的输入电压范围为3.0V至4.2V,输出电压范围为0.8V至3.9875V,峰值电流分别高达1.3A、1.3A和3.5A。三个300mA pMOS…...
十年JAVA搬砖路——Linux搭建Ldap服务器。
1.安装命令 yum -y install openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel2.启动ldap systemctl start slapd systemctl enable slapd3.修改密码 slappasswd Aa123456获得返回的密码加密密码串: {SSHA}DkSw0…...
论文 辅助笔记:t2vec train.py
1 train 1.1 加载training和validation数据 def train(args):logging.basicConfig(filenameos.path.join(args.data, "training.log"), levellogging.INFO)设置了日志的基本配置。将日志信息保存到名为 "training.log" 的文件中日志的级别被设置为 INFO&…...
同时标注分割、检测、多分类属性的工具
1、 https://blog.csdn.net/minstyrain/article/details/82385580/ 2、 https://zhuanlan.zhihu.com/p/656703406...
LeetCode75——Day24
文章目录 一、题目二、题解 一、题目 2390. Removing Stars From a String You are given a string s, which contains stars *. In one operation, you can: Choose a star in s. Remove the closest non-star character to its left, as well as remove the star itself.…...
B端企业形象设计的正确姿势,你学会了吗?
如今,企业形象设计在B端市场中变得越来越重要。它是企业与客户之间建立联系的桥梁,也是吸引目标客户的重要方式。为了帮助您打造一个独特而专业的企业形象设计,我将为您提供十个步骤。 步骤1:了解企业定位和目标 在设计B端企业形…...
我在Vscode学OpenCV 基本的加法运算
根据上一篇我们可知__图像的属性 链接:《我在Vscode学OpenCV 处理图像》 属性— API 形状 img.shape 图像大小 img.size 数据类型 img.dtype shape:如果是彩色图像,则返回包含行数、列数、通道数的数组;如果是二值图像或者灰度…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
