C++打造局域网聊天室第十课: 客户端编程及数据发送
文章目录
- 前言
- 一、补充内容,设置显示框换行
- 二、客户端编程
- 三、封装消息发送函数
- 四、所处的身份状态
- 总结
前言
C++打造局域网聊天室第十课: 客户端编程及数据发送
一、补充内容,设置显示框换行
编辑框的显示内容默认是不会换行的,这样会使得显示的客户端发送内容很乱,在显示编辑框的属性中找到如下两个内容,将默认的FALSE设置为TRUE,则显示框的内容会自动换行。


二、客户端编程
回顾:客户端编程流程:
TCP服务端:WSASartup, socket, bind, listen, accept, read, write, closesocket, WSACleanup
TCP客户端:WSASartup, socket, connect, read, write, closesocket, WSACleanup
与服务器端类似,当点击连接服务器按钮后,才是客户端的身份。同样添加时间处理程序


在chartroom.h头文件中会自动声明

在chartroom.cpp源文件中会自动出现函数实现框架

同样为了避免阻塞现象的发生,利用异步I/O模型和多线程来处理。在chartroom.h头文件中声明客户端连接服务端线程的返回句柄。

并在chartroom.cpp源文件中的构造函数处初始化,并创建客户端连接服务端线程。

void CchartroomDlg::OnBnClickedButton1() // 单击连接服务器的MFC消息映射机制
{// TODO: 在此添加控件通知处理程序代码m_hConnectThread = CreateThread(NULL, 0, ConnectThreadFunc, this, 0, NULL); // 创建新线程函数,客户端连接服务端线程
}
与服务端类似,创建新的头文件和源文件实现客户端的编程,并在相应的源文件中#include"Client.h"。



下面在Client.cpp中实现函数ConnectThreadFunc()。首先需要添加一些CchartroomDlg类的成员变量,在chartroom.h头文件中声明。

同样在chartroom.cpp源文件中的构造函数处初始化

由于在服务端已经写过SOCKET_Select函数,这里直接在Client.h中声明即可

在Client.cpp源文件实现函数DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
DWORD WINAPI ConnectThreadFunc(LPVOID pParam)
{CchartroomDlg* pChartRoom = (CchartroomDlg*)pParam; // 将参数强制转化为主对话框类,以便使用主对话框类的一些成员变量ASSERT(pChartRoom != NULL); // 如果pChartRoom为空指针则程序中断// 新建pChartRoom->m_ConnectSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (pChartRoom->m_ListenSock == INVALID_SOCKET) // 如果新建失败{AfxMessageBox(_T("新建SOCKET失败!"));return FALSE;}// 连接服务端CString strServIp; // 由于使用Unicode编码,程序将CString视为宽字节pChartRoom->GetDlgItemText(IDC_IPADDRESS1, strServIp); // 获取界面上的IP地址int iPort = pChartRoom->GetDlgItemInt(IDC_EDIT6); // 获取界面上的端口if (iPort <= 0 || iPort > 65535) // 对端口值进行判断{AfxMessageBox(_T("请输入合适的端口:1-65535"));goto __Error_End;}// 进行本机字节顺序与网络字节顺序的转换char szIpAddr[16] = { 0 }; // 定义窄字节数组,是为了让inet_addr()函数使用,该函数输入只能为窄字节USES_CONVERSION; // 与T2A配套使用strcpy_s(szIpAddr, 16, T2A(strServIp)); //将宽字节转化为窄字节,T2A将工程所用的编码格式转化为窄字节。strcpy_s函数做窄字节字符串的拷贝//将端口和IP地址等信息放入sockaddr_in结构中sockaddr_in service;service.sin_family = AF_INET; //与新建socket第一个参数的值一样service.sin_addr.s_addr = inet_addr(szIpAddr); // 将IP地址传递给sin_addr.s_addr成员service.sin_port = htons(iPort); //将端口传递给sin_port成员,htons为字节顺序转换函数,利用该函数是因为常用的CUP字节顺序与网络字节顺序相反// 例如地址0x12345678,host:0x78 0x56 0x34 0x12; net:0x12 0x34 0x56 0x78。h为host(主机),n为network。htons即为将主机的字节顺序转化为net顺序// connect函数:第一个参数为一个socket;第二个参数为一个sockaddr*结构(WinSock1版本中,等同于Winsock2版本中的sockaddr_in);第三个参数为第二个参数的长度if (connect(pChartRoom->m_ConnectSock, (struct sockaddr*)&service, sizeof(struct sockaddr)) == SOCKET_ERROR){AfxMessageBox(_T("连接失败,请重试!"));goto __Error_End;}pChartRoom->ShowMsg(_T("系统信息:连接服务器成功!"));while (1){if (SOCKET_Select(pChartRoom->m_ConnectSock, 100, TRUE)) // 异步I/O模型{TCHAR szBuf[MAX_BUF_SIZE] = { 0 };int iRet = recv(pChartRoom->m_ConnectSock, (char*)szBuf, MAX_BUF_SIZE, 0);if (iRet > 0){//正确,接收数据成功pChartRoom->ShowMsg(szBuf); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示}else // 接收数据失败,有错误或者服务器端关闭了{// 关闭socketpChartRoom->ShowMsg(_T("聊天室服务器已停止,请重新进行连接!")); //利用在chartroom.cpp中实现的ShowMsg方法将信息显示break;// 跳出循环,客户端已下线,退出线程}}Sleep(500);}__Error_End:closesocket(pChartRoom->m_ConnectSock);return TRUE;
}
三、封装消息发送函数
发送消息通过点击发送消息按键实现

同样添加该控件的单击MFC消息映射机制,这里不再赘述
为了实现消息的发送功能,封装一个CchartroomDlg类的成员函数SendClientMsg(),具体实现如下,注意,同样需要先在chartroomDlg.h头文件中声明,然后再在源文件中实现:

// 说明:某一个客户端发送消息,将信息发送给队列中除了发消息客户端外的所有客户端,第一个参数为发送的内容;第二个参数为发消息的客户端
void CchartroomDlg::SendClientMsg(CString strMsg, CClientitem *pNotSend) // 实现发送消息函数.发消息给客户端
{TCHAR szBuf[MAX_BUF_SIZE] = { 0 };_tcscpy_s(szBuf, MAX_BUF_SIZE, strMsg);for (INT_PTR idx = 0; idx < m_ClientArray.GetCount(); idx++){if (!pNotSend || pNotSend->m_Socket != m_ClientArray.GetAt(idx).m_Socket || pNotSend->hThread != m_ClientArray.GetAt(idx).hThread ||pNotSend->m_surlp != m_ClientArray.GetAt(idx).m_surlp){// 第一个参数为要发送给哪个客户端的socket;第二个参数为发送内容;第三个参数为发送内容的长度*每个字符占几个字节send(m_ClientArray.GetAt(idx).m_Socket, (char*)szBuf, _tcslen(szBuf)*sizeof(TCHAR), 0);}}
}
四、所处的身份状态
服务端和客户端对应的功能是不一样的,因此我们需要一个功能来区分客户端和服务端,以便采取正确的处理过程
注意:该程序有三种状态:刚启动时既不是客户端也不是服务端,客户端,服务端
在chartroomDlg.h头文件中声明一个整形变量来区分三种状态。

同样在构造函数中进行初始化

监听成功后,证明此时该程序为服务端身份

调用connect连接成功后,证明该程序此时为客户端身份

之后完成点击发送消息的MFC消息映射机制函数实现
void CchartroomDlg::OnBnClickedButton5() // 单击发送消息的MFC消息映射机制
{// TODO: 在此添加控件通知处理程序代码CString strMsg;GetDlgItemText(IDC_EDIT4, strMsg); // 获取输入信息编辑框内的输入信息if (m_bIsServer == TRUE) // 若本程序状态为服务器{strMsg = _T("服务器:>") + strMsg;ShowMsg(strMsg);SendClientMsg(strMsg, NULL); // 将信息发送给所有队列中的客户端}else if (m_bIsServer == FALSE) // 若本程序状态为客户端{CString strTmp = _T("本地客户端: > ") + strMsg;ShowMsg(strTmp);int iSend = send(m_ConnectSock, (char*)strMsg.GetBuffer(), strMsg.GetLength() * sizeof(TCHAR), 0);strMsg.ReleaseBuffer();}SetDlgItemText(IDC_EDIT4, _T("")); // 将信息发送给服务端后清空发送内容编辑框}
上述代码实现了服务端将信息发送给所有客户端,以及客户端将信息发送给服务端的功能。此外,当一个客户端将信息发送给服务端后,服务端还需要将该信息转发给其他所有客户端。这部分功能需要在服务端程序Server.cpp中添加。

此外,还需要实现一个功能:只有当聊天信息输入框里面有信息时,发送信息按键才是可点击状态,否则为不可点击。具体操作按图即可。


EN_CHANGE为当编辑框中内容发生改变才触发相应函数
void CchartroomDlg::OnEnChangeEdit4() // // 实现当输入聊天信息编辑框内容发生变化时调用的函数
{// TODO: 如果该控件是 RICHEDIT 控件,它将不// 发送此通知,除非重写 CDialogEx::OnInitDialog()// 函数并调用 CRichEditCtrl().SetEventMask(),// 同时将 ENM_CHANGE 标志“或”运算到掩码中。// TODO: 在此添加控件通知处理程序代码CString strMsg;GetDlgItemText(IDC_EDIT4, strMsg); // 获取输入聊天信息编辑框内容if (strMsg.IsEmpty()) // 如果没有聊天信息{EnableWindow(IDC_BUTTON5, 0); // 禁用发送信息按键}else // // 如果有聊天信息{EnableWindow(IDC_BUTTON5, 1); // 启用发送信息按键}
}
同时在初始化时,要设置发送信息按钮为不可用



总结
C++打造局域网聊天室第十课: 客户端编程及数据发送
相关文章:
C++打造局域网聊天室第十课: 客户端编程及数据发送
文章目录 前言一、补充内容,设置显示框换行二、客户端编程三、封装消息发送函数四、所处的身份状态总结 前言 C打造局域网聊天室第十课: 客户端编程及数据发送 一、补充内容,设置显示框换行 编辑框的显示内容默认是不会换行的,这…...
Nginx整合Lua脚本
Nginx-Lua Nginx整合Lua脚本 Lua环境搭建 下载地址 linux环境下 yum install lua安装后验证 lua -vLua脚本执行 lua xxx.luaNginx整合Lua nginx需要添加lua模块 嵌入内容 示例如下 修改nginx.conf如下 location /lua {default_type text/plain;content_by_lua ngx.sa…...
【C++】C++11 STL容器emplace方法原理剖析
在 C 11 STL 容器中,push/insert > emplace 新的方法,push 和 emplace 的区别在于: 1. push push 通常用于将一个元素添加到容器的末尾(在 std::vector、std::deque 等序列容器中),或者在关联容器中插入…...
QT-简单视觉框架代码
文章目录 简介1. 整体架构2. 关键类功能概述3. 详细代码实现hikcameraworker.h 和 hikcameraworker.cpp(海康相机工作线程类)imageviewerwidget.h 和 imageviewerwidget.cpp(图像查看部件类)构造函数 ImageViewerWidget析构函数 ~…...
AI新书推荐:深度学习和大模型原理与实践(清华社)
本书简介 在这个信息爆炸、技术革新日新月异的时代,深度学习作为人工智能领域的重要分支,正引领着新一轮的技术革命。《深度学习和大模型原理与实践》一书,旨在为读者提供深度学习及其大模型技术的全面知识和实践应用的指南。 本书特色在于…...
[spring]处理器
我们可以通过spring来管理我们的类,之后我们可以通过spring的容器来获取我们所需要的Bean类对象。Spring的处理器是Spring对外开发的重要扩展点,它允许我们介入到Bean的整个实例化流程中来,可以动态添加、修改BeanDefinition、动态修改Bean 首…...
重温设计模式--中介者模式
中介者模式介绍 定义:中介者模式是一种行为设计模式,它通过引入一个中介者对象来封装一系列对象之间的交互。中介者使得各个对象之间不需要显式地相互引用,从而降低了它们之间的耦合度,并且可以更方便地对它们的交互进行管理和协调…...
重温设计模式--设计模式七大原则
文章目录 1、开闭原则(Open - Closed Principle,OCP)定义:示例:好处: 2、里氏替换原则(Liskov Substitution Principle,LSP)定义:示例:好处&#…...
LeetCode429周赛T4
最小化二进制字符串中最长相同子字符串的长度 在处理二进制字符串问题时,优化字符串结构以满足特定条件是一项常见的挑战。本文将探讨一个具体的问题:给定一个长度为 n 的二进制字符串 s 和一个整数 numOps,通过最多 numOps 次位翻转操作&am…...
详解MySQL在Windows上的安装
目录 查看电脑上是否安装了MySQL 下载安装MySQL 打开MySQL官网,找到DOWNLOADS 然后往下翻,找到MySQL Community(GPL) Downloads>> 然后找到MySQL Community Server 然后下载,选择No thanks,just start my download. 然后双击进行…...
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
本教程的知识点为:操作系统 1. 常见的操作系统 4. 小结 ls命令选项 2. 小结 mkdir和rm命令选项 1. mkdir命令选项 压缩和解压缩命令 1. 压缩格式的介绍 2. tar命令及选项的使用 3. zip和unzip命令及选项的使用 4. 小结 编辑器 vim 1. vim 的介绍 2. vim 的工作模式 …...
软件测试面试题和简历模板(面试前准备篇)
一、问题预测 1、让简单介绍下自己(这个不用说了每次面试开场) 面试官,你好,我叫xxx,xx年本科毕业,从事软件测试将近3年的时间。在此期间做过一些项目也积累过一些经验,能够独立地完成软件测试…...
Linux 基本使用和程序部署
1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种: 直接安装在物理机上。但是Linux桌面使用起来非常不友好,所以不建议。[不推荐]。使用虚拟机软件,将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bugÿ…...
uniapp微信小程序,使用fastadmin完成一个一键获取微信手机号的功能
前端部分 点击按钮,获取手机号 <button open-type"getPhoneNumber" getphonenumber"bindGetPhoneNumber" hover-class"none"class"btn-purity">一键获取</button> 传入openid和code bindGetPhoneNumber(e) …...
CSS系列(27)- 图形与滤镜详解
前端技术探索系列:CSS 图形与滤镜详解 🎨 致读者:探索CSS的艺术表现力 👋 前端开发者们, 今天我们将深入探讨 CSS 图形和滤镜效果,学习如何创建引人注目的视觉效果。 基础图形 🚀 几何形状…...
Docker 技术系列之安装多版本Mysql5.6和Mysql5.7
image 大家好,后面的就不是关于MAC专有的内容,基本是跟Java环境,基础技术方面有关。所以这个教程对于在linux系统还是macOS都是通用的,不用担心。 上一篇,我们安装好对应的Docker之后,感受到了它的便利。接…...
理解并使用Linux 内核中的 Tracepoint
理解并使用Linux 内核中的 Tracepoint 1. 引言 1.1 为什么需要 Tracepoint? 在内核调试与性能分析中,传统的 printk 方法虽然简单直接,但存在几个显著的局限性: 日志噪音:printk 会将所有输出无差别地记录到系统日…...
centos7中Gbase8s数据库安装,以及数据导入遇到的一系列问题
centos7中Gbase8s数据库安装,以及遇到的一系列问题 以下是我在centos7上安装gbase8s数据库遇到的一系列问题,包括数据库安装,数据导入,数据连接,不能完全作为标准,只可作为类似问题参考,有问题…...
AW36518芯片手册解读(3)
接前一篇文章:AW36518芯片手册解读(2) 二、详述 3. 功能描述 (1)上电复位 当电源电压VIN降至预定义电压VPOR(典型值为2.0V)以下时,该设备会产生复位信号以执行上电复位操作&#x…...
MySQL的REPEATABLE READ事务隔离级别
本文隔离级别: T1内读T2的update数据 首先开两个事务(左二) 事务1修改成李四,提交 事务2再读还是张三,也就是说,记录的数据从事务开始时一直到结束,读的都是同一个版本,读不到T2未提交的此条记录修改&…...
QMCDecode:打破音乐格式壁垒的解密技术实现
QMCDecode:打破音乐格式壁垒的解密技术实现 【免费下载链接】QMCDecode QQ音乐QMC格式转换为普通格式(qmcflac转flac,qmc0,qmc3转mp3, mflac,mflac0等转flac),仅支持macOS,可自动识别到QQ音乐下载目录,默认转换结果存储…...
Golang如何做Helm Chart_Golang Helm教程【秒懂】
Go二进制在scratch/alpine镜像报“no such file or directory”是因CGO默认启用导致动态链接libc,需禁用CGO并静态编译;Helm配置须统一管理探针路径、环境变量、镜像tag等四端一致。Go二进制进镜像总报 no such file or directory?不是镜像没…...
AzurLaneAutoScript:从人工操作到智能决策的游戏自动化革命
AzurLaneAutoScript:从人工操作到智能决策的游戏自动化革命 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 深夜…...
专业的成都全铝家具哪家好
在寻找成都专业的全铝家具定制服务时,四川方与圆铝作全铝家具有限公司无疑是值得考虑的优质选择。这家位于成都华阳滨江天樾2栋27楼2号的专业工作室,以其八年匠心深耕和独特优势,在成都全铝家具市场中脱颖而出。为什么选择方与圆铝作…...
MySQL 常用数据类型的系统总结
一、数值型(存储数字,含整数、小数、布尔值)1. 整数类型(INT 系列)数据类型字节数取值范围(有符号)取值范围(无符号)核心特性适用场景TINYINT1-128 ~ 1270 ~ 255占用空间…...
Java字符串相似度计算:10大算法库终极指南
Java字符串相似度计算:10大算法库终极指南 【免费下载链接】java-string-similarity Implementation of various string similarity and distance algorithms: Levenshtein, Jaro-winkler, n-Gram, Q-Gram, Jaccard index, Longest Common Subsequence edit distanc…...
为资源管理器文件右键菜单增加 使用 Web 搜索 功能
欢迎使用右键搜。这是一个使用 Autoit v3 编写的右键菜单增强小插件,用于在资源管理器文件右键菜单中增加一键搜索,让您快速调用在线搜索引擎查找与此文件相关的信息。 在整理文件时,经常需要上网搜一下某个文件的背景资料、相关信息。虽然“…...
个人开发者如何评估一个AI Token代理服务商的技术实力?
作为个人开发者,评估 AI Token 代理服务商(API 中转平台)的技术实力,核心是“把黑盒变灰盒”。不要只看价格和宣传,要通过可观测性、兼容性、容错机制三个维度进行实战验证。一、基础兼容性:接口规范与模型…...
L6470步进电机驱动库:嵌入式高精度运动控制实现
1. L6470驱动库技术深度解析:面向工业级步进电机控制的嵌入式底层实现1.1 芯片级认知:L6470为何成为高精度运动控制的硬件基石L6470是STMicroelectronics推出的智能步进电机驱动芯片,采用QFN32封装,集成双H桥功率级、12位ADC、SPI…...
EncoderButton库解析:嵌入式旋转编码器与按键事件驱动方案
1. EncoderButton 库深度解析:面向嵌入式工程师的事件驱动型旋转编码器与按键一体化解决方案1.1 库定位与工程价值EncoderButton 是一个专为 Arduino 和 Teensy 平台设计的轻量级、事件驱动型外设抽象库,其核心目标是在不丢失任何物理事件的前提下&#…...
