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未提交的此条记录修改&…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...