client网络模块的开发和client与server端的部分联动调试

客户端网络模块的开发

我们需要先了解socket通信的流程

socket通信

server端的流程

client端的流程

对于closesocket()函数来说

closesocket()是用来关闭套接字的,将套接字的描述符从内存清除,并不是删除了那个套接字,只是切断了联系,所以我们如果重复调用,不closesocket()就会报错

创建网络模块类

我们依然采用的是单例模式

学会套用server端网络模块类的代码

添加一个ClientSocket类

对于这里面代码的修改

我们修改初始化代码

	bool InitSocket(const std::string& strIPAddress) {if (m_sock != INVALID_SOCKET) CloseSocket();m_sock = socket(PF_INET, SOCK_STREAM, 0);//TODO,校验if (m_sock == -1) return false;sockaddr_in serv_adr; //服务器地址memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(strIPAddress.c_str());serv_adr.sin_port = htons(9527);if (serv_adr.sin_addr.s_addr == INADDR_NONE) {AfxMessageBox("指定的ip地址,不存在");return false;}int ret = connect(m_sock, (sockaddr*)&serv_adr, sizeof(serv_adr));if (ret == -1) {AfxMessageBox("连接失败!!!重新连接");TRACE("连接失败,%d %s\r\n", WSAGetLastError(), GetErrInfo(WSAGetLastError()).c_str());return false;}return true;}

然后我们删除了accept类

然后我们客户端连接失败我们需要打印出连接失败的原因

WSAGetLastError()

使用 WSAGetLastError() 函数 来获得上一次的错误代码

返回值指出了该线程进行的上一次 Windows Sockets API 函数调用时的错误代码

WSAGetLastError()函数返回值表格,在下面文章里面

“WSAGetLastError()使用”讲解

然后我们需要一个可以将错误码格式化的函数

这个函数不用深究,记住这个模板以后直接用

std::string GetErrInfo(int wsaErrCode)
{std::string ret;LPVOID lpMsgBuf = NULL; //这个函数需要自己开辟缓冲区FormatMessage( //系统函数,把错误码格式化的函数FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,NULL,wsaErrCode,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR)&lpMsgBuf, 0, NULL);ret = (char*)lpMsgBuf;LocalFree(lpMsgBuf); //Free()掉return ret; //把这个缓冲区地址返回出去
}

然后咱们需要编辑client界面

对于这个我们不用添加类,直接双击那个连接测试,就会自动生成一个类在Dlg文件中,因为我们一开始创建这个项目时候就给client图形化界面了

void CRemoteClientDlg::OnBnClickedBtnTest()
{ClientSocket *pClient = ClientSocket::getInstance();bool ret = pClient->InitSocket("127.0.0.1");//后续加返回值的处理if (!ret) {AfxMessageBox("网络初始化失败!!!");return;}CPacket pack(1981,NULL,0);ret = pClient->Send(pack);TRACE("Send ret %d\r\n",ret);int cmd  = pClient->DealCommand();TRACE("ack:%d\r\n",cmd);pClient->CloseSocket();
}

server端和client端联动调试

启动客户端时候会报一个错

添加上那个报错信息

我们需要加上处理包的while循环

            CserverSocket* pserver = CserverSocket::getInstance();int count = 0;if (pserver->InitSocket() == false) {MessageBox(NULL, _T("网络初始化异常未能成功初始化,请检查网络状"), _T("网络初始化异常未能成功初始化,请检查网络状态"), MB_OK | MB_ICONERROR);exit(0);}while (CserverSocket::getInstance() != NULL) { // 相当于while(true)if (pserver->AcceptClient() == false) {if (count >= 3) {MessageBox(NULL, _T("多次无法正常接入程序,结束程序"), _T("接入用户失败!"), MB_OK | MB_ICONERROR);exit(0);}MessageBox(NULL, _T("无法正常接入用户,自动重试"), _T("接入用户失败!"), MB_OK | MB_ICONERROR);count++;}TRACE("AcceptClient true");int ret = pserver->DealCommand(); //获取命令TRACE("DealCommand ret:%d", ret);if (ret > 0) {ret = ExcuteCommand(ret);if (ret != 0) {TRACE("执行命令失败%d ret = %d\r\n", pserver->GetPacket().sCmd, ret);}pserver->CloseClient(); //短连接}}

ExcuteCommand()

int ExcuteCommand(int nCmd)
{int ret;switch (nCmd) {case 1://查看磁盘分区ret = MakeDriveInfo();break;case 2: //查看指定目录下的文件ret = MakeDirectoryInfo();break;case 3: //打开文件ret = RunFile();break;case 4: //下载文件ret = DownloadFile();break;case 5://鼠标操作ret = MouseEvent();break;case 6://发送屏幕内容 ==>发送屏幕的截图ret = SendScreen();break;case 7://锁机上锁(网吧可以用上)ret = LockMC();break;case 8:ret = UnlockMC();//解锁break;case 1981:ret = TestConnect();break;}return ret;
}

case 1981:是我们用来测试包的

int TestConnect()
{CPacket pack(1981, NULL, 0);CserverSocket::getInstance()->Send(pack);return 0;}
//CRemoteClientDlg::OnBnClickedBtnTest()函数	CPacket pack(1981,NULL,0);ret = pClient->Send(pack);TRACE("Send ret %d\r\n",ret);int cmd  = pClient->DealCommand(); //这也仅仅是为了测试TRACE("ack:%d\r\n",cmd);pClient->CloseSocket();

设置双项目调试启动

然后将两个项目的操作地方设置为启动,也可以设置为不调试启动

然后我们使用TRACE来追踪调试信息

我们还需要注意一点 ,就是server端初始化socket(初始化自己的socket等别人连接,基本上是不用改变的),可以等到析构时候在closesocket掉,但是client端不一样,client端可能需要连接不同的server端,所以需要不断的Init,也就是需要不断的将套接字和server端的IP连接

所以server端的m_sock初始化可以放在构造函数里面,client端的m_sock初始化需要放在Init函数里面,同时需要在里面closesocket

你会发现就算终止了,但是这个socket并没有close

证据

再次运行一遍,然后单步

你会发现程序进入了closesocket()函数,代表m_sock并不是INVALID_SOCKET

在遥远的2002年,有程序员碰到了同样的问题

然后就是我们选择持久连接还是非持久连接

client是我们在操控,向服务器发命令很少(间隔几秒),每次都是一个包,所以client端对server端应该采用非持久的连接,也就是

在每次包发完都进行pClient->CloseSocket();关闭socket连接

但是我们client端会向server端请求下载文件,远程桌面之类的命令,我们肯定不止要接收一个包,所以server端对client端要采用长连接

这里面我们需要注意野指针引起的内存泄漏问题

野指针引起的内存泄漏

内存泄漏是指我们在堆中申请(new/malloc)了一块内存,但是没有去手动的释放(delete/free)内存,导致指针已经消失,而指针指向的东西还在,已经不能控制这块内存,

例子

void remodel(std::string &str)
{//创建了一个局部指针变量,函数调用结束后,指针变量消失,但堆中内存仍然被占用,没有被释放,导致内存泄漏std::string *ps = new std::string(str); //内存泄漏了
}

如果发生了内存泄露又没有及时发现,随着程序运行时间的增加,程序越来越大,直到消耗完系统的所有内存,然后系统崩溃

在我们那个DealCommand()函数里面

我们申请了缓冲区去recv由那个套接字收到的数据

但是我们一开始设计时候是为了长连接作准备的,因为我们考虑的是双方都能收到很多包

因为client对server是短连接,所以server端的deal函数只用处理一个包,可以随着过程释放new出来的空间

server端的deal函数

	int DealCommand() { //无限循环if (m_client == -1) return -1;//char buffer[1024] = "";char* buffer = new char[4096]; //if (buffer == NULL) {TRACE("内存不足");return -2;}memset(buffer, 0, 4096);size_t index = 0;while (true) {size_t len = recv(m_client, buffer+index, 4096-index, 0);if (len <= 0) {delete[]buffer;return -1;}TRACE("recv %d\r\n", len);index += len; //可能收到2000个字节的包len = index;m_packet = CPacket ((BYTE*)buffer, len);if (len > 0) {memmove(buffer, buffer + len, 4096-len);//从buffer + len复制4096-len个字节到bufferindex -= len; //可能只用1000个delete[]buffer;return m_packet.sCmd;}}delete[]buffer;return -1;}

client端的deal函数,我们需要处理server端发来的很多包,但是我们又要防止内存泄漏,我们也不知道server端一次性给client端发了多少包,就不知道在这个函数哪个地方释放掉这个内存,但是我们知道的是client端的socket释放时候,我们那个包肯定处理完了,所以我们搞一个成员变量,让其在析构时候自动delete掉,我们想到了vecter,随对象的释放而析构

private: std::vector<char> m_buffer; //也是用的new,内存不需要管理,可以直接取地址用ClientSocket() {if (InitSockEnv() == FALSE) {MessageBox(NULL, _T("无法初始化套接字环境,请检查网络设置"), _T("初始化错误!!!"), MB_OK | MB_ICONERROR);exit(0);}m_buffer.resize(4096); //设置大小}
	int DealCommand() { //无限循环if (m_sock == -1) return -1;//char buffer[1024] = "";char* buffer = m_buffer.data(); //memset(buffer, 0, 4096);size_t index = 0;while (true) {size_t len = recv(m_sock, buffer + index, 4096 - index, 0);if (len <= 0) {return -1;}index += len; //可能收到2000个字节的包len = index;m_packet = CPacket((BYTE*)buffer, len); if (len > 0) {memmove(buffer, buffer + len, 4096 - len);//从buffer + len复制4096-len个字节到bufferindex -= len; //可能只用1000个return m_packet.sCmd;}}return -1;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/52238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决图片导入Excel后变成横向问题

最近有同事遇到图片打开的时候是竖向的&#xff0c;导入Excel后就变成横向了 我在网上搜了一下&#xff0c;没找到直接的答案 我猜大概是用了某些软件做处理&#xff08;例如压缩分辨率&#xff09;但是没处理干净 后来经过多次尝试&#xff0c;发现只要用windows自带的画图软件…

FouriDown: Factoring Down-Sampling into Shuffling and Superposing

摘要 https://openreview.net/pdf?idnCwStXFDQu 空间下采样技术&#xff0c;如步长卷积、高斯下采样和最近邻下采样&#xff0c;在深度神经网络中至关重要。在本文中&#xff0c;我们重新审视了空间下采样家族的工作机制&#xff0c;并分析了先前方法中使用的静态加权策略所…

【头条的AI IDE MarsCode】

文章目录 1、AI云端IDE2、网址3、可以直接导入github上的仓库代码4、总结 1、AI云端IDE 操作很简单&#xff0c;打开豆包MarsCode 云端 IDE&#xff0c;通过 GitHub 链接导入 Cambrian-1 代码库&#xff0c;向豆包MarsCode 提问。让豆包MarsCode 介绍 Cambrian-1 项目&#xf…

安卓主板_MTK联发科主板定制开发|PCBA定制开发

MTK联发科安卓主板&#xff0c;采用MT6762八核平台方案&#xff0c;支持谷歌Android 11.0系统&#xff0c;MT6762采用ARM八核A53内核芯片、主频高达2.0GHz&#xff0c;GPU采用ARM PowerVR GE8329650MHZ&#xff0c;支持主流19201080分辨率&#xff0c;支持硬解H.264&#xff0c…

十分钟教会你在python中使用Redis

文章目录 一、Redis 简介二、Redis 特性三、Redis 使用场景四、Redis 客户端与常用命令五、Redis在Python上的操作1. 安装 redis-py 库2. 连接Redis服务器3. 数据操作字符串操作设置键值对 set(key, string)获取字符串 get(key)检查键是否exists(key)删除键delete(key)批量设置…

Win10安装ChatTTS-2024-cuda10.1

0x00 前言 ChatTTS是专门为对话场景设计的文本转语音模型&#xff0c;例如大语言助手对话任务。它支持英文和中文两种语言。最大的模型使用了10万小时以上的中英文数据进行训练。目前在huggingface中的开源版本为4万小时训练且未SFT的版本。 0x01 准备环境 版本操作系统Win1…

鸿蒙内核源码分析(忍者ninja篇) | 都忍者了能不快吗

ninja | 忍者 ninja是一个叫 Evan Martin的谷歌工程师开源的一个自定义的构建系统,最早是用于 chrome的构建,Martin给它取名 ninja(忍者)的原因是因为它strikes quickly(快速出击).这是忍者的特点,可惜Martin不了解中国文化,不然叫小李飞刀更合适些.究竟有多块呢? 用Martin自…

Uniapp使用InnerAudioContext返回内部 audio 上下文 ,获取不到duration当前音频的长度,如何解决?

原因:其实是wx的bug,这个问题在开发环境中&#xff0c;并不会遇到。而是真机测试或发版后&#xff0c;就获取不到duration。 解决:我们只要去手动播放音频后&#xff0c;就会抓取到duration。 代码示例 关键代码 this.audioContext.play(); //手动播放音频 完整代码 Audio…

本地Docker部署Navidrome音乐服务器与远程访问听歌详细教程

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

StarRocks 存算分离数据回收原理

前言 StarRocks存算分离表中&#xff0c;垃圾回收是为了删除那些无用的历史版本数据&#xff0c;从而节约存储空间。考虑到对象存储按照存储容量收费&#xff0c;因此&#xff0c;节约存储空间对于降本增效尤为必要。 在系统运行过程中&#xff0c;有以下几种情况可能会需要删…

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 07数据中心的边缘

本章将帮助你回答以下问题 可以用哪些方式将 Clos 拓扑连接到外部网终?边缘部署路由协议的最佳实践是什么?企业应如何处理混合云中的连接? 连接模型 为什么要连接到外部世界? 数据中心连接到外部世界的原因很多。如果你要对外提供某种服务(例如搜索服务广告推荐系统或内…

35岁程序员转行大模型:如何把握行业机遇与个人发展

对于一位35岁的程序员想要转行到大模型领域&#xff0c;这是一个很好的时机&#xff0c;因为人工智能和大模型技术正在快速发展&#xff0c;并且有着广泛的应用前景。以下是一些具体的步骤和建议&#xff0c;可以帮助您顺利地完成这一转变&#xff1a; 基础知识学习 数学基础&a…

科技在日常生活中的革新

在科技日新月异的今天&#xff0c;‌我们的生活正经历着前所未有的变革。‌从智能家居到可穿戴设备&#xff0c;‌科技已经渗透到我们生活的每一个角落&#xff0c;‌深刻地影响着我们的生活方式和社会经济的发展。‌ 智能家居系统的出现&#xff0c;‌无疑是科技改变生活的典…

[鹏城杯 2022]简单的php

题目源代码 <?phpshow_source(__FILE__); $code $_GET[code]; if(strlen($code) > 80 or preg_match(/[A-Za-z0-9]|\|"||\ |,|\.|-|\||\/|\\|<|>|\$|\?|\^|&|\|/is,$code)){die( Hello); }else if(; preg_replace(/[^\s\(\)]?\((?R)?\)/, , $code…

探索HarmonyOS中的列表组件及其自定义特性

在现代移动应用中&#xff0c;List组件是数据列表的关键元素。HarmonyOS中的List组件不仅具备传统的列表功能&#xff0c;还提供了丰富的自定义选项&#xff0c;允许开发者根据需求灵活调整列表的行为和外观展示。本文将探讨HarmonyOS中列举组件的自定义特性&#xff0c;包括自…

深度剖析C++string(上篇)

目录 前言 1.C string类 2.string类中的常见构造 3.string类对象的容量操作 4.. string类对象的访问及遍历操作 5. auto和范围for(补充&#xff09; auto关键字 范围for 结束语 前言 C语言我们学习了字符串和字符串的相关函数&#xff0c;在C语言中&#xff0c;字符串是…

C++如何为枚举量生成对应的解释:4种常见的方法

C如何为枚举量生成对应的解释 在 C 中&#xff0c;你可以通过几种方法为枚举量生成对应的解释或描述文本。以下是几种常见的方法&#xff1a; 1. 使用 switch 语句 这是最直接的方法&#xff0c;通过 switch 语句为每个枚举值返回一个对应的字符串。 #include <iostream…

day45-dynamic programming-part12-8.16

tasks for today: 1. 115.不同的子序列 2. 583.两个字符串选的删除操作 3. 72.编辑距离 4. 总结编辑两个序列关系的问题 ------------------------------------------------------------------- 1. 115.不同的子序列 In this practice, it is necessary to compare with t…

10 Java数据结构:包装类、数组(Array工具类)、ArrayList

文章目录 前言一、包装类1、Integer&#xff08;1&#xff09;基本用法&#xff08;2&#xff09;JDK5前的包装类用法&#xff08;了解即可&#xff0c;能更好帮助我们理解下面的自动装箱和自动拆箱机制&#xff09;&#xff08;3&#xff09;自动装箱与自动拆箱机制 --- 导致&…

【学习笔记】Day 21

一、进度概述 1、机器学习常识19-22&#xff0c;以及相关代码复现 二、详情 19、矩阵分解 矩阵分解是一个纯数学问题&#xff0c;但当给矩阵赋予现实意义后&#xff0c;矩阵分解就成为了使用数学应对机器学习问题的一类典型而巧妙的方法。 在线性回归分析中&#xff…