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

MFC网络通信-Udp服务端

目录

1、UI的布局

2、代码的实现:

(1)、自定义的子类CServerSocket

(2)、重写OnReceive事件

(3)、在CUdpServerDlg类中处理

(4)、在OnInitDialog函数中

(5)、实现自定义函数ProcessPendingRead()处理接收到的数据

(6)、加入新客户的消息先进性判断是否位enter

(7)、加入的新消息如果是leave

(8)、普通信息


1、UI的布局

添加一个CServerSocket类继承于CSocket

2、代码的实现:

(1)、自定义的子类CServerSocket

所有的显示应该显示在框架中,在构造函数传入一个框架的指针然后进行初始化

class CUdpServerDlg;//声明一下dlg类

public:
    CServerSocket(CUdpServerDlg* pdlg);//所有的操作显示在dlg上面
    ~CServerSocket();
private:
    CUdpServerDlg *m_pMainDlg;//指针来接收

CServerSocket::CServerSocket(CUdpServerDlg* pdlg)//传递的消息统一放在对话框中处理,所以初始化传递一个对话框指针
{    
    this->m_pMainDlg = pdlg;
}

(2)、重写OnReceive事件

(父类的OnReceive是纯虚函数,如果有数据可读就会调用该方法)

protected:
    virtual void OnReceive(int nErrorCode);//重写onReceive()方法,如果有数据可读就会调用该方法
    //在WINSOCKET父类中有这个纯虚函数来接收数据
 

//有数据可读就会调用这个纯虚函数
void CServerSocket::OnReceive(int nErrorCode)
{
    CSocket::OnReceive(nErrorCode);
    /*该行代码的作用是确保底层的数据接收和处理机制正常运行,并在此基础上执行自定义的数据处理逻辑。*/
    m_pMainDlg->ProcessPendingRead();//消息处理统一放在对话框

    
}

(3)、在CUdpServerDlg类中处理

struct ClientAddr//自定义结构体存放IP和端口号
{
    CString strIP;
    UINT inPort;
};

    CServerSocket *m_pServerSocket;
    CArray<ClientAddr,ClientAddr&>m_ClientAddList;//客户端发送的所有消息
    /*,m_ClientAddList是一个包含ClientAddr类型对象的数组,可以使用Add()方法向其中添加元素,使用GetAt()方法访问已添加的元素。这个数组被用来存储客户端连接的地址信息等数据。*/
    void ProcessPendingRead();

(4)、在OnInitDialog函数中


    m_pServerSocket = new CServerSocket(this);

在当前对话框中需要使用到通信(SOCKET),所以在堆区创建一个CServerSocket的对象,并且还要这个对象和当前窗口相关联,然后用一个m_pServerSocket指针指向新建的对象。
    m_pServerSocket->Create(8080, SOCK_DGRAM);//用于创建一个 UDP 套接字,并将其绑定到本地 IP 地址和指定的端口号(这里是 8080)上。

(5)、实现自定义函数ProcessPendingRead()处理接收到的数据

初始化接收数据数组和客户端结构体对象

    TCHAR buffer[4096];//接收数据的数组
    ClientAddr clietAddr;//客户端结构体对象

判断读取的内容的是否有效

int nRead = m_pServerSocket->ReceiveFrom(buffer, 4096, clietAddr.strIP, clietAddr.inPort);//接收数据的字节数nRead
    //缓冲区地址,接收数据大小,客户端的IP,客户端的端口
    if (nRead == SOCKET_ERROR)//如果读出错
    {
        return;
    }

如果内容有效字符串结尾加上\0,并且转换类型位CString类型

buffer[nRead] = L'\0';

//在接收到的数据后面加上结束符(索引0开始,所以nRead代表最后一位加1)
    CString strTemp(buffer);

//char *类型的buffer转成CString类型的strTemp,代表接收到的内容。

strTemp是接收到的消息,消息又分为三种,加入新客户,删除客户,还有就是普通消息

(6)、加入新客户的消息先进性判断是否位enter

if (strTemp.CompareNoCase(_T("enter"))==0)

/*比较 strTemp 和 "enter" 是否相等,不区分大小写。如果相等,则返回 0,否则返回一个非零值*/

在条件下我们将客户加入到列表中

/把新的客户加入到列表中
        m_ClientAddList.Add(clietAddr);

通知其它客户端有用户加入

首先加入的信息需要规范一下

CString strEnterMsg;
  strEnterMsg.Format(_T("系统消息:%s(%d)进入了房间"), clietAddr.strIP, clietAddr.inPort);

通知其他客户端

for (i = 0;i<m_ClientAddList.GetSize();i++)//通知所有的客户端
        {
            ClientAddr& tempClient = m_ClientAddList.ElementAt(i);//获取所有的客户端
            m_pServerSocket->SendTo(strEnterMsg, strEnterMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);//发送消息
        }

控件上的显示更新(人数的更新还有内容的更新)

SetDlgItemInt(IDC_EDIT_NUMBER, m_ClientAddList.GetSize());//当前人数在文本上设置
        //之前的消息可能存在,需要拿出来放在alMsg的后面
        
        CString alMsg;
        GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);//取消消息到alMsg
        SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strEnterMsg);//将新的消息叠加到alMsg后面

(7)、加入的新消息如果是leave

if (strTemp.CompareNoCase(_T("leave")) == 0) 

//离开房间

从列表中遍历寻找要删除的用户

    //列表中移除用户
        for (i = 0;i<m_ClientAddList.GetSize();i++)
{
            ClientAddr& tempClient = m_ClientAddList.ElementAt(i);//遍历每一个
    if (tempClient.inPort == clietAddr.inPort&&tempClient.strIP.Compare(clietAddr.strIP) == 0)
   {
                break;
                //遍历找到了要移除的端口号和IP都相等的tempClient,break
   }
  }

一旦找到用户break,然后删除用户

if (i<m_ClientAddList.GetSize())//如果遍历没完就说明找到了。
        {
            m_ClientAddList.RemoveAt(i);//移除
        }

格式化发送的消息

CString strLeaveMsg;
        strLeaveMsg.Format(_T("通知消息:%s(%d)离开了房间"), clietAddr.strIP, clietAddr.inPort);//通知的内容
        

遍历发送所有的客户端

    for (i = 0; i < m_ClientAddList.GetSize(); i++)//通知每一个客户端有用户离开了
        {
            ClientAddr& tempClient = m_ClientAddList.ElementAt(i);
            m_pServerSocket->SendTo(strLeaveMsg, strLeaveMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);
        }

更新控件上的内容

        SetDlgItemInt(IDC_EDIT_NUMBER, m_ClientAddList.GetSize());//更新当前人数
        CString alMsg;
        GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);
        SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strLeaveMsg);//更新消息

(8)、普通信息

else//普通的聊天信息
    {
        CString strMsg;
        strMsg.Format(_T("%s(%d):%s"), clietAddr.strIP, clietAddr.inPort,strTemp);//格式化普通聊天信息的内容
        
        for (i = 0; i < m_ClientAddList.GetSize(); i++)
        {    //转发所有的人消息
            ClientAddr& tempClient = m_ClientAddList.ElementAt(i);
            m_pServerSocket->SendTo(strMsg, strMsg.GetLength() + 1000, tempClient.inPort, tempClient.strIP);
        }
        
        CString alMsg;
        GetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg);
        SetDlgItemText(IDC_EDIT_CHAT_MESSAGE, alMsg + _T("\r\n") + strMsg);//更新聊天框的内容
    }

(3)、优化处理

初始化默认服务端的端口和IP在框架的入口函数

//设置服务端默认的端口和IP
    SetDlgItemText(IDC_EDIT_SERVER_IP, L"127.0.0.1");
    SetDlgItemText(IDC_EDIT_SERVER_PORT, L"8080");

初始化按钮的一些状态

没有加入房间,发送按钮和退出按钮都不能点击

//设置按钮初始化状态
    GetDlgItem(IDC_BUTTON_OUT)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);

初始化发送消息的EDIT只读,服务端IP和PORT可以修改

GetDlgItem(IDC_EDIT_SEND_MESSAGE)->EnableWindow(FALSE);
    GetDlgItem(IDC_EDIT_SERVER_IP)->EnableWindow(TRUE);
    GetDlgItem(IDC_EDIT_SERVER_PORT)->EnableWindow(TRUE);

加入房间之后重新设置按钮的状态

//设置按钮文本框的状态
    //设置按钮初始化状态
    GetDlgItem(IDC_BUTTON_OUT)->EnableWindow(TRUE);
    GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(TRUE);

    ((CEdit*)GetDlgItem(IDC_EDIT_SEND_MESSAGE))->SetReadOnly(FALSE);
    ((CEdit*)GetDlgItem(IDC_EDIT_SERVER_IP))->SetReadOnly(TRUE);
    ((CEdit*)GetDlgItem(IDC_EDIT_SERVER_PORT))->SetReadOnly(TRUE);

离开房间重新设置按钮的状态

GetDlgItem(IDC_BUTTON_OUT)->EnableWindow(FALSE);
    GetDlgItem(IDC_BUTTON_SEND)->EnableWindow(FALSE);

    ((CEdit*)GetDlgItem(IDC_EDIT_SEND_MESSAGE))->SetReadOnly(TRUE);
    ((CEdit*)GetDlgItem(IDC_EDIT_SERVER_IP))->SetReadOnly(FALSE);
    ((CEdit*)GetDlgItem(IDC_EDIT_SERVER_PORT))->SetReadOnly(FALSE);

重写框架类的关闭窗口函数

实现关闭窗口之后调用离开房间按钮事件。


BOOL CUdpClientDlg::DestroyWindow()
{
    // TODO: 在此添加专用代码和/或调用基类
    if (m_bEnterRoom)
    {
        OnBnClickedButtonOut();
    }

    return CDialogEx::DestroyWindow();
}

相关文章:

MFC网络通信-Udp服务端

目录 1、UI的布局 2、代码的实现&#xff1a; &#xff08;1&#xff09;、自定义的子类CServerSocket &#xff08;2&#xff09;、重写OnReceive事件 &#xff08;3&#xff09;、在CUdpServerDlg类中处理 &#xff08;4&#xff09;、在OnInitDialog函数中 &#xff0…...

最简单且有效的msvcp140.dll丢失的解决方法,有效的解决msvcp140.dll丢失

在我们使用电脑的过程中&#xff0c;有时会遇到一些令人困扰的问题&#xff0c;如msvcp140.dll文件丢失。对于许多不熟悉这方面技术的小伙伴来说&#xff0c;遇到msvcp140.dll丢失的问题可能会觉得棘手。其实这是一个很常见的问题&#xff0c;并且解决起来并不复杂。接下来将给…...

HBase理论与实践-基操与实践

基操 启动&#xff1a; ./bin/start-hbase.sh 连接 ./bin/hbase shell help命令 输入 help 然后 <RETURN> 可以看到一列shell命令。这里的帮助很详细&#xff0c;要注意的是表名&#xff0c;行和列需要加引号。 建表&#xff0c;查看表&#xff0c;插入数据&#…...

内存管理设计精要

系统设计精要是一系列深入研究系统设计方法的系列文章&#xff0c;文中不仅会分析系统设计的理论&#xff0c;还会分析多个实际场景下的具体实现。这是一个季更或者半年更的系列&#xff0c;如果你有想要了解的问题&#xff0c;可以在文章下面留言。 持久存储的磁盘在今天已经不…...

Java——StringBuffer与StringBuilder的区别

Java——StringBuffer与StringBuilder的区别 StringBuffer和StringBuilder是Java中用于处理字符串的两个类&#xff0c;它们之间的主要区别在于线程安全性和性能方面。 1. 线程安全性&#xff1a; StringBuffer&#xff1a;StringBuffer 是线程安全的&#xff0c;所有的公共方…...

基于深度学习的菠萝与果叶视觉识别及切断机构设计

收藏和点赞&#xff0c;您的关注是我创作的动力 文章目录 概要 一、课题内容二、总体方案确定2.1 方案选择2.2 菠萝的视觉识别流程2.3 菠萝果叶切断机构设计流程 三 基于深度学习的菠萝检测模型3.1 卷积神经网络简介3.2 YOLO卷积神经网络3.3 图像采集与数据制作3.4 数据训练与…...

springboot整合七牛云oss操作文件

文章目录 springboot整合七牛云oss操作文件核心代码&#xff08;记得修改application.yml配置参数⭐&#xff09;maven依赖QiniuOssProperties配置类UploadControllerResponseResult统一封装响应结果ResponseType响应类型枚举OssUploadService接口QiniuOssUploadServiceImpl实现…...

跨国传输的常见问题与对应解决方案

在今天的全球化时代&#xff0c;跨国数据传输已经成为一个不可或缺的需求。不论是个人还是企业&#xff0c;都需要通过网络将文件或数据从一个国家传输到另一个国家&#xff0c;以实现信息共享、协作、备份等目的。然而&#xff0c;跨国数据传输并不是一项容易的任务&#xff0…...

Git(七).git 文件夹瘦身,GitLab 永久删除文件

目录 一、问题背景二、问题复现2.1 新建项目2.2 上传大文件2.3 上传结果 三、解决方案3.1 GitLab备份与还原1&#xff09;备份2&#xff09;还原 3.2 删除方式一&#xff1a;git filter-repo 命令【推荐】1&#xff09;安装2&#xff09;删除本地仓库文件3&#xff09;重新关联…...

多线程锁的升级原理是什么

在 Java 中&#xff0c;锁共有 4 种状态&#xff0c;级别从低到高依次为&#xff1a;无状态锁&#xff0c;偏向锁&#xff0c;轻量级锁和重量级锁状态&#xff0c;这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。 多线程锁锁升级过程 如下图所示 多线程锁的升级过程…...

金山文档轻维表之删除所有行记录

目前脚本文档里面的只有删除行记录功能&#xff0c;但是需要指定ID值&#xff0c;不能实现批量删除&#xff0c;很多人反馈但是官方无回应&#xff0c;挺奇怪的 但是批量删除的需求我很需要&#xff0c;最后研究了一下&#xff0c;还是挺容易实现的 测试&#xff1a; 附上脚本…...

站坑站坑站坑站坑站坑

站坑站坑站坑站坑站坑站坑站坑...

在Vue中,你可以使用动态import()语法来动态加载组件

在Vue中&#xff0c;你可以使用动态import()语法来动态加载组件。动态导入允许你在需要时异步加载组件&#xff0c;这样可以提高应用程序的初始加载性能。 下面是一个使用动态导入加载组件的示例&#xff1a; <template> <div> <button click"loadComp…...

金蝶云星空表单插件获取日期控件判空处理(代码示例)

文章目录 金蝶云星空表单插件获取日期控件判空处理C#实现 金蝶云星空表单插件获取日期控件判空处理 C#实现 DateTime? deliveryDate (DateTime?)this.View.Model.GetValue("FApproveDate");//审核日期long leadtime 20;//天数if (!deliveryDate.IsNullOrEmpty()…...

通过xshell传输文件到服务器

一、user is not in the sudoers file. This incident will be reported. 参考链接&#xff1a; [已解决]user is not in the sudoers file. This incident will be reported.(简单不容易出错的方式)-CSDN博客 简单解释下就是&#xff1a; 0、你的root需要设置好密码 sudo …...

centos7.9编译安装python3.7.2

联网环境下编译安装python3.7.2&#xff0c;不联网则需要配置cnetos7.9离线源 下载解压软件包 [rootlocalhost ~]# tar -xf Python-3.7.3.tar.gz [rootlocalhost ~]# ls anaconda-ks.cfg Python-3.7.3 Python-3.7.3.tar.gz [rootlocalhost ~]# [rootlocalhost ~]# cd Pytho…...

【教3妹学编程-算法题】2913. 子数组不同元素数目的平方和 I

-----------------第二天------------------------ 面试官 : 好的&#xff0c; 我们再来做个算法题吧。平时工作中会尝试用算法吗&#xff0c; 用到了什么数据结构&#xff1f; 3妹 : 有用到&#xff0c; 用到了 bla bla… 面试官 : 好的&#xff0c; 题目是这样的&#xff1…...

是否会有 GPT-5 的发布?

本心、输入输出、结果 文章目录 是否会有 GPT-5 的发布?前言围绕 GPT-5 的信息OpenAI 期待增长GPT-5 - 到底是真的在训练,还是一个虚构的故事Sam Altman字里行间包含的信息我们在什么时候可以期待 GPT-5 的发布GPT-5 预计将在哪些方向努力GPT-5 在听觉领域GPT-5 在视频处理领…...

使用 Selenium Python 检查元素是否存在

像 Selenium 这样的自动化工具使我们能够通过不同的语言和浏览器自动化 Web 流程并测试应用程序。 Python 是它支持的众多语言之一&#xff0c;并且是一种非常简单的语言。 它的Python客户端帮助我们通过Selenium工具与浏览器连接。 Web 测试对于开发 Web 应用程序至关重要&am…...

const迭代器与模板构造函数

在自己实现C中list的时候&#xff0c;当实现const迭代器的时候&#xff0c;发现报错了&#xff0c;一直思考到现在 才发现是一个&#xff0c;很简单的问题&#xff0c;但是也让我有了一点感受&#xff0c;我在这里给大家分享一下。文章目录 1.当时遇到的问题2.解决方法3. 自己的…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说&#xff0c;传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度&#xff0c;通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...