当前位置: 首页 > news >正文

使用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完成端口实现简单回显服务器&#xff0c;因为是测试用的&#xff0c;所以代码很粗糙。 提醒 使用的是ReadFile、WriteFile来实现Overlapped IO&#xff0c;正式场合应该用WSARecv、WSASend&#xff0c;原因&#xff1a;来自《Windows网络编程技术》 8.2.5节 在这里…...

【ROS】Nav2源码之nav2_behavior_tree详解

1、简介 nav2_bt_navigator实现ROS2节点以行为树的方式来处理。 nav2_behavior_tree模块提供如下功能: 一个c模板类&#xff0c;可以轻松地将ROS2 动作(actions)和服务(services)集成到行为树(Behavior Trees)中。特定于导航的行为树节点。通用的BehaviorTreeEngine类&#…...

SpringBoot---myBatis数据库操作

一&#xff0c;分页查询 现在controller中设置参数&#xff0c;RequestParam(defaultValue "1") 设置默认值 GetMapping public Result page(RequestParam(defaultValue "1") Integer page,RequestParam(defaultValue "10") Integer pageSize…...

力扣541.反转字符串II

原题链接&#xff1a;力扣541.反转字符串II 思路&#xff1a; 其实在遍历字符串的过程中&#xff0c;只要让 i (2 * k)&#xff0c;i 每次移动 2 * k 就可以了&#xff0c;然后判断是否需要有反转的区间。 因为要找的也就是每2 * k 区间的起点&#xff0c;这样写&#xff0c…...

撕掉Hadoop标签,Cloudera未来可期吗?

Cloudera&#xff1a;大数据的弄潮儿 1、Cloudera发展史2、透过Cloudera看清大数据时代的转变3、参考文章 1、Cloudera发展史 说起Cloudera&#xff0c;就不得不提起Hadoop&#xff0c;Cloudera的过去就是Hadoop时代中的一个缩影。作为全球第一家也是最大一家Hadoop商业化公司&…...

排序算法(1)

这里写目录标题 排序插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序向下调整堆排序 交换排序冒泡排序 排序 插入排序 直接插入排序 直接插入排序是O&#xff08;N^2&#xff09;的排序算法 从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. 无重复字符的最长子串

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…...

元素的水平居中和垂直几种方案

总结一下各种元素的水平居中和垂直居中方案。 水平居中&#xff1a; 1.行内元素水平居中 text-align: center 定义行内内容&#xff08;例如文字&#xff09;如何相对它的块父元素对齐;不仅可以让文字水平居中&#xff0c;还可以让行内元素水平居中 注意&#xff1a;给行内…...

JS和JQuery的区别

JS和jQuery都是用于前端开发的工具&#xff0c;但是它们有一些重要的区别。主要区别如下&#xff1a; JS是一种编程语言&#xff0c;而jQuery是一个JS库。JS可以与其他语言一起使用&#xff08;如PHP、Python等&#xff09;&#xff0c;而jQuery是JS的一个扩展&#xff0c;只能…...

延时摄影视频制作工具 LRTimelapse mac中文版特点介绍

lrTimelapse mac是一款适用于 Windows 和 macOS 系统的延时摄影视频制作软件&#xff0c;可以帮助用户创建高质量的延时摄影视频。该软件提供了直观的界面和丰富的功能&#xff0c;支持多种时间轴摄影工具和文件格式&#xff0c;并具有高度的可定制性和扩展性。 lrTimelapse ma…...

Mac电脑怎么运行 Office 办公软件

虽然 Office 软件也有 Mac 版本的&#xff0c;但是有蛮多小伙伴用起来还是感觉不得劲&#xff0c;毕竟接触了太久的 Windows&#xff0c;所以想要使用 Windows 版本的 Office 软件。 今天就给大家介绍一下怎么在 Mac 电脑中运行 Windows 版本的办公软件&#xff0c;在这里就需…...

FPGA 如何 固化程序到 FLASH中

1、导出Hardware 2、导出bit文件 3、打开SDK 4、 点击Ok 5、创建工程 6、 输入工程名称&#xff1a;guhua 7、选择 Zynq FSBL 8、单击 guhua、然后点击 build 点击&#xff1a;build all 9、 右键之后&#xff0c;点击&#xff1a;Creat Boot Image 10、点击 Cr…...

电源管理(PMIC)MAX20428ATIA/VY、MAX20428ATIC/VY、MAX20428ATIE/VY适合汽车ADAS应用的开关稳压器

一、概述 MAX20428是一款高效率、八路输出、低压PMIC。OUT1将输入电源升压至5V&#xff0c;电流高达500mA&#xff0c;而三个同步降压转换器的输入电压范围为3.0V至4.2V&#xff0c;输出电压范围为0.8V至3.9875V&#xff0c;峰值电流分别高达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获得返回的密码加密密码串&#xff1a; {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端企业形象设计的正确姿势,你学会了吗?

如今&#xff0c;企业形象设计在B端市场中变得越来越重要。它是企业与客户之间建立联系的桥梁&#xff0c;也是吸引目标客户的重要方式。为了帮助您打造一个独特而专业的企业形象设计&#xff0c;我将为您提供十个步骤。 步骤1&#xff1a;了解企业定位和目标 在设计B端企业形…...

我在Vscode学OpenCV 基本的加法运算

根据上一篇我们可知__图像的属性 链接&#xff1a;《我在Vscode学OpenCV 处理图像》 属性— API 形状 img.shape 图像大小 img.size 数据类型 img.dtype  shape&#xff1a;如果是彩色图像&#xff0c;则返回包含行数、列数、通道数的数组&#xff1b;如果是二值图像或者灰度…...

Midjourney新艺术风格突然失效?92%用户忽略的--stylize冲突机制与3步回滚修复法

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Midjourney新艺术风格突然失效&#xff1f;92%用户忽略的--stylize冲突机制与3步回滚修复法 近期大量用户反馈&#xff1a;在 Midjourney v6.1 中启用高 stylize 值&#xff08;如 --stylize 1000&#xff0…...

抖音批量下载解决方案:模块化架构与智能降级策略

抖音批量下载解决方案&#xff1a;模块化架构与智能降级策略 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖…...

全栈开发简历:避免 “样样通样样松”,突出核心技术栈

一、开篇暴击:你的全栈简历,可能正在被HR当“笑话”看 “熟练掌握HTML、CSS、JavaScript、Python、Java、PHP、MySQL、MongoDB、AWS、Docker、K8s……” 当你在简历上敲下这串“技术彩虹屁”时,是不是觉得自己就是传说中“一人顶一个团队”的全栈大神?醒醒!某互联网公司…...

深度解密:如何彻底掌控Windows Defender的系统级权限与持久化配置

深度解密&#xff1a;如何彻底掌控Windows Defender的系统级权限与持久化配置 【免费下载链接】defender-control An open-source windows defender manager. Now you can disable windows defender permanently. 项目地址: https://gitcode.com/gh_mirrors/de/defender-con…...

Potree加载点云实战:从CloudCompare检查到浏览器3D展示的全链路避坑

Potree点云加载全流程实战&#xff1a;从数据验收到3D可视化的深度指南 点云数据正逐渐成为三维地理信息系统、建筑信息模型和数字孪生领域的核心载体。作为开源点云可视化库的佼佼者&#xff0c;Potree以其高效的Web端渲染能力赢得了众多开发者的青睐。然而在实际项目集成过程…...

技术架构深度剖析:如何构建专业的浏览器资源嗅探扩展

技术架构深度剖析&#xff1a;如何构建专业的浏览器资源嗅探扩展 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在现代浏览器扩展开发领域&#x…...

git常用使用命令(亲测,可以,自己的笔记)

一本 官方中文版 书分享给大家&#xff08;说明&#xff1a;本人多次阅读&#xff0c;体会是容易入门&#xff0c;读起来很顺手&#xff0c;但是讲的不深入&#xff09; https://git-scm.com/book/zh/v2 一、git官方使用命令&#xff1a; usage: git [--version] [--help] [-C…...

利用Taotoken为Claude Code配置稳定后备API解决封号与Token不足问题

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 利用Taotoken为Claude Code配置稳定后备API解决封号与Token不足问题 对于依赖Claude Code进行日常开发的工程师而言&#xff0c;服…...

保姆级教程:在Windows 11上用Mosquitto搭建你的第一个MQTT服务器(含开机自启和用户管理)

Windows 11环境下Mosquitto MQTT服务器全流程部署指南 在物联网项目开发初期&#xff0c;本地搭建MQTT服务器进行原型测试是每个开发者都会经历的环节。作为轻量级的消息传输协议&#xff0c;MQTT凭借其低功耗、低带宽占用和高效的发布/订阅机制&#xff0c;已成为智能家居、工…...

QMCDecode终极指南:三步解锁QQ音乐加密文件,让你的音乐自由播放

QMCDecode终极指南&#xff1a;三步解锁QQ音乐加密文件&#xff0c;让你的音乐自由播放 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac&#xff0c;qmc0,qmc3转mp3, mflac,mflac0等转flac)&#xff0c;仅支持macOS&#xff0c;可自动识别到QQ音乐下载目…...