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

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++打造局域网聊天室第十课: 客户端编程及数据发送

文章目录 前言一、补充内容&#xff0c;设置显示框换行二、客户端编程三、封装消息发送函数四、所处的身份状态总结 前言 C打造局域网聊天室第十课&#xff1a; 客户端编程及数据发送 一、补充内容&#xff0c;设置显示框换行 编辑框的显示内容默认是不会换行的&#xff0c;这…...

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 容器中&#xff0c;push/insert > emplace 新的方法&#xff0c;push 和 emplace 的区别在于&#xff1a; 1. push push 通常用于将一个元素添加到容器的末尾&#xff08;在 std::vector、std::deque 等序列容器中&#xff09;&#xff0c;或者在关联容器中插入…...

QT-简单视觉框架代码

文章目录 简介1. 整体架构2. 关键类功能概述3. 详细代码实现hikcameraworker.h 和 hikcameraworker.cpp&#xff08;海康相机工作线程类&#xff09;imageviewerwidget.h 和 imageviewerwidget.cpp&#xff08;图像查看部件类&#xff09;构造函数 ImageViewerWidget析构函数 ~…...

AI新书推荐:深度学习和大模型原理与实践(清华社)

本书简介 在这个信息爆炸、技术革新日新月异的时代&#xff0c;深度学习作为人工智能领域的重要分支&#xff0c;正引领着新一轮的技术革命。《深度学习和大模型原理与实践》一书&#xff0c;旨在为读者提供深度学习及其大模型技术的全面知识和实践应用的指南。 本书特色在于…...

[spring]处理器

我们可以通过spring来管理我们的类&#xff0c;之后我们可以通过spring的容器来获取我们所需要的Bean类对象。Spring的处理器是Spring对外开发的重要扩展点&#xff0c;它允许我们介入到Bean的整个实例化流程中来&#xff0c;可以动态添加、修改BeanDefinition、动态修改Bean 首…...

重温设计模式--中介者模式

中介者模式介绍 定义&#xff1a;中介者模式是一种行为设计模式&#xff0c;它通过引入一个中介者对象来封装一系列对象之间的交互。中介者使得各个对象之间不需要显式地相互引用&#xff0c;从而降低了它们之间的耦合度&#xff0c;并且可以更方便地对它们的交互进行管理和协调…...

重温设计模式--设计模式七大原则

文章目录 1、开闭原则&#xff08;Open - Closed Principle&#xff0c;OCP&#xff09;定义&#xff1a;示例&#xff1a;好处&#xff1a; 2、里氏替换原则&#xff08;Liskov Substitution Principle&#xff0c;LSP&#xff09;定义&#xff1a;示例&#xff1a;好处&#…...

LeetCode429周赛T4

最小化二进制字符串中最长相同子字符串的长度 在处理二进制字符串问题时&#xff0c;优化字符串结构以满足特定条件是一项常见的挑战。本文将探讨一个具体的问题&#xff1a;给定一个长度为 n 的二进制字符串 s 和一个整数 numOps&#xff0c;通过最多 numOps 次位翻转操作&am…...

详解MySQL在Windows上的安装

目录 查看电脑上是否安装了MySQL 下载安装MySQL 打开MySQL官网&#xff0c;找到DOWNLOADS 然后往下翻&#xff0c;找到MySQL Community(GPL) Downloads>> 然后找到MySQL Community Server 然后下载&#xff0c;选择No thanks,just start my download. 然后双击进行…...

【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】

本教程的知识点为&#xff1a;操作系统 1. 常见的操作系统 4. 小结 ls命令选项 2. 小结 mkdir和rm命令选项 1. mkdir命令选项 压缩和解压缩命令 1. 压缩格式的介绍 2. tar命令及选项的使用 3. zip和unzip命令及选项的使用 4. 小结 编辑器 vim 1. vim 的介绍 2. vim 的工作模式 …...

软件测试面试题和简历模板(面试前准备篇)

一、问题预测 1、让简单介绍下自己&#xff08;这个不用说了每次面试开场&#xff09; 面试官&#xff0c;你好&#xff0c;我叫xxx&#xff0c;xx年本科毕业&#xff0c;从事软件测试将近3年的时间。在此期间做过一些项目也积累过一些经验&#xff0c;能够独立地完成软件测试…...

Linux 基本使用和程序部署

1. Linux 环境搭建 1.1 环境搭建方式 主要有 4 种&#xff1a; 直接安装在物理机上。但是Linux桌面使用起来非常不友好&#xff0c;所以不建议。[不推荐]。使用虚拟机软件&#xff0c;将Linux搭建在虚拟机上。但是由于当前的虚拟机软件(如VMWare之类的)存在一些bug&#xff…...

uniapp微信小程序,使用fastadmin完成一个一键获取微信手机号的功能

前端部分 点击按钮&#xff0c;获取手机号 <button open-type"getPhoneNumber" getphonenumber"bindGetPhoneNumber" hover-class"none"class"btn-purity">一键获取</button> 传入openid和code bindGetPhoneNumber(e) …...

CSS系列(27)- 图形与滤镜详解

前端技术探索系列&#xff1a;CSS 图形与滤镜详解 &#x1f3a8; 致读者&#xff1a;探索CSS的艺术表现力 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS 图形和滤镜效果&#xff0c;学习如何创建引人注目的视觉效果。 基础图形 &#x1f680; 几何形状…...

Docker 技术系列之安装多版本Mysql5.6和Mysql5.7

image 大家好&#xff0c;后面的就不是关于MAC专有的内容&#xff0c;基本是跟Java环境&#xff0c;基础技术方面有关。所以这个教程对于在linux系统还是macOS都是通用的&#xff0c;不用担心。 上一篇&#xff0c;我们安装好对应的Docker之后&#xff0c;感受到了它的便利。接…...

理解并使用Linux 内核中的 Tracepoint

理解并使用Linux 内核中的 Tracepoint 1. 引言 1.1 为什么需要 Tracepoint&#xff1f; 在内核调试与性能分析中&#xff0c;传统的 printk 方法虽然简单直接&#xff0c;但存在几个显著的局限性&#xff1a; 日志噪音&#xff1a;printk 会将所有输出无差别地记录到系统日…...

centos7中Gbase8s数据库安装,以及数据导入遇到的一系列问题

centos7中Gbase8s数据库安装&#xff0c;以及遇到的一系列问题 以下是我在centos7上安装gbase8s数据库遇到的一系列问题&#xff0c;包括数据库安装&#xff0c;数据导入&#xff0c;数据连接&#xff0c;不能完全作为标准&#xff0c;只可作为类似问题参考&#xff0c;有问题…...

AW36518芯片手册解读(3)

接前一篇文章&#xff1a;AW36518芯片手册解读&#xff08;2&#xff09; 二、详述 3. 功能描述 &#xff08;1&#xff09;上电复位 当电源电压VIN降至预定义电压VPOR&#xff08;典型值为2.0V&#xff09;以下时&#xff0c;该设备会产生复位信号以执行上电复位操作&#x…...

MySQL的REPEATABLE READ事务隔离级别

本文隔离级别&#xff1a; T1内读T2的update数据 首先开两个事务&#xff08;左二&#xff09; 事务1修改成李四,提交 事务2再读还是张三&#xff0c;也就是说&#xff0c;记录的数据从事务开始时一直到结束&#xff0c;读的都是同一个版本,读不到T2未提交的此条记录修改&…...

sqoop的参数有哪些?

Sqoop 是一款用于在 Hadoop 与关系型数据库之间进行数据传输的工具&#xff0c;它有很多参数&#xff0c;可分为通用参数、导入参数和导出参数等&#xff0c;以下是一些常见的参数介绍&#xff1a; 通用参数 --connect 说明&#xff1a;指定要连接的关系型数据库的 JDBC URL。…...

动态规划<四> 回文串问题(含对应LeetcodeOJ题)

目录 引例 其余经典OJ题 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 引例 OJ 传送门Leetcode<647>回文子串 画图分析&#xff1a; 使用动态规划解决 原理&#xff1a;能够将所有子串是否是回文的信息保存在dp表中 在使用暴力方法枚举出所有子串&#xff0c;是…...

跨模态知识迁移:基于预训练语言模型的时序数据建模

在NLP和CV领域&#xff0c;通常通过在统一的预训练模型上进行微调&#xff0c;能够在各自领域的下游任务中实现SOTA&#xff08;最先进&#xff09;的结果。然而&#xff0c;在时序预测领域&#xff0c;由于数据量相对较少&#xff0c;难以训练出一个统一的预训练模型来覆盖所有…...

重温设计模式--职责链模式

文章目录 职责链模式的详细介绍C 代码示例C示例代码2 职责链模式的详细介绍 定义与概念 职责链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它旨在将请求的发送者和多个接收者解耦&#xff0c;让多个对象都有机会处理请求&a…...

git冲突解决

git冲突解决 最近遇到了一次git冲突的问题 起因是因为最近公司数据推送部分重构&#xff0c;负责重构的同事就改动了我的一小部分推送的代码&#xff0c;然后等我开发完合并到远程master的时候&#xff0c;报了merge冲突。我对于git工具确实不是很熟练&#xff0c;只是学习了…...

Java学习笔记(14)--面向对象编程

面向对象基础 学习资料来自多态 - Java教程 - 廖雪峰的官方网站 目录 面向对象基础 Override 多态 举个例子 覆写Object方法 调用super final 练习 小结 Override 在继承关系中&#xff0c;子类如果定义了一个与父类方法签名完全相同的方法&#xff0c;被称为覆写&…...

《Swift 字面量》

《Swift 字面量》 介绍 在 Swift 编程语言中&#xff0c;字面量是一种表示源代码中固定值的表达方式。字面量可以直接表示数字、字符串、布尔值等基本数据类型&#xff0c;为编程提供了简洁和直观的方式。Swift 支持多种类型的字面量&#xff0c;包括整数字面量、浮点数字面量…...

数据库 SQL 常用语句全解析

数据库 SQL 常用语句全解析 在数据库领域&#xff0c;SQL&#xff08;Structured Query Language&#xff09;作为标准语言&#xff0c;掌控着数据的查询、插入、更新与删除等关键操作。无论是新手入门数据库&#xff0c;还是经验丰富的开发者日常工作&#xff0c;熟练掌握 SQ…...

SQLite 命令

关于《SQLite 命令》的文章&#xff0c;我可以为您概述一些关键点。SQLite是一个轻量级的数据库管理系统&#xff0c;它被广泛用于各种应用程序中。SQLite命令主要分为两类&#xff1a;一类是SQL命令&#xff0c;另一类是SQLite特定的点命令。 SQL命令&#xff1a;这些命令用于…...

本地如何启动casdoor

1、下载代码 GitHub - casdoor/casdoor at v1.777.0 下载对应tag的代码&#xff0c;我这里选择的时v1.777.0版本 通过网盘分享的文件&#xff1a;casdoor-1.777.0.zip 链接: https://pan.baidu.com/s/1fPNqyJYeyfZnem_LtEc0hw 提取码: avpd 2、启动后端 1、使用goland编译…...