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();
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

38基于matlab的期货预测,利用PSO优化SVM和未优化的SVM进行对比,得到实际输出和期望输出结果。

基于matlab的期货预测&#xff0c;利用PSO优化SVM和未优化的SVM进行对比&#xff0c;得到实际输出和期望输出结果。线性核函数、多项式、RBF核函数三种核函数任意可选&#xff0c;并给出均方根误差&#xff0c;相对误差等结果&#xff0c;程序已调通&#xff0c;可直接运行。 3…

后端设计PG liberty的作用和增量式生成

Liberty&#xff08;俗称LIB和DB&#xff09;&#xff0c;是后端设计中重要的库逻辑描述文件&#xff0c;这里边包含了除过physical&#xff08;当然也有一点点涉及&#xff09;以外所有的信息&#xff0c;对整个后端设计实现有非常大的作用。借此机会&#xff0c;一起LIB做一个…

AN动画基础——遮罩动画

【AN动画基础——遮罩动画】 什么是遮罩动画基本使用方法实战&#xff1a;水墨遮罩 本篇内容&#xff1a;了解遮罩动画 重点内容&#xff1a;遮罩动画应用 工 具&#xff1a;Adobe Animate 2022 什么是遮罩动画 遮罩动画是一种常见的图形效果&#xff0c;利用遮罩层来实现元素…

[双指针] (三) LeetCode LCR 179. 查找总价格为目标值的两个商品 和 15. 三数之和

[双指针] (三) LeetCode LCR 179. 查找总价格为目标值的两个商品 和 15. 三数之和 文章目录 [双指针] (三) LeetCode LCR 179. 查找总价格为目标值的两个商品 和 15. 三数之和查找总价格为目标值的两个商品题目分析解题思路代码实现总结 三数之和题目分析解题思路代码实现总结 …

【Java 进阶篇】Java Response 路径详解

在Java Web开发中&#xff0c;处理HTTP响应的路径是一个重要的概念。了解如何正确处理和管理路径对于构建健壮的Web应用程序至关重要。本篇博客将详细介绍Java中的HTTP响应路径&#xff0c;包括路径的组成、相对路径和绝对路径的区别、如何构建和处理路径&#xff0c;以及路径在…

项目知识点总结-住房图片信息添加-Excel导出

&#xff08;1&#xff09;住房信息添加 Controller&#xff1a; RequestMapping("/add")public String add(Home home, Model model) throws IOException{String sqlPath null;//定义文件保存的本地路径String localPath"D:\\AnZhuang\\Java项目\\选题\\Xin-…

设计模式(单例模式、工厂模式及适配器模式、装饰器模式)

目录 0 、设计模式简介 一、单例模式 二、工厂模式 三、适配器模式 四、装饰器模式 0 、设计模式简介 设计模式可以分为以下三种: 创建型模式&#xff1a;用来描述 “如何创建对象”&#xff0c;它的主要特点是 “将对象的创建和使用分离”。包括单例、原型、工厂方法、…

141. 环形链表、Leetcode的Python实现

博客主页&#xff1a;&#x1f3c6;看看是李XX还是李歘歘 &#x1f3c6; &#x1f33a;每天分享一些包括但不限于计算机基础、算法等相关的知识点&#x1f33a; &#x1f497;点关注不迷路&#xff0c;总有一些&#x1f4d6;知识点&#x1f4d6;是你想要的&#x1f497; ⛽️今…

数字孪生技术与VR:创造数字未来

在当今数字化浪潮中&#xff0c;数字孪生和虚拟现实&#xff08;VR&#xff09;技术是两大亮点&#xff0c;它们以独特的方式相互结合&#xff0c;为各个领域带来了创新和无限可能。本篇文章将探讨数字孪生与VR之间的关系&#xff0c;以及它们如何共同开辟未来的新前景。 数字…

数字化如何赋能企业降本增效?

在当前高度不确定的市场环境下&#xff0c;降本增效已成为传统企业热议的话题。在这个背景下&#xff0c;企业内部各种“卷”现象层出不穷&#xff0c;各部门都在积极降本、开源节流&#xff0c;同时也在争夺本就不足的企业资源。因此&#xff0c;数字部门在资源受限的情况下&a…

【Amazon】跨AWS账号资源授权存取访问

文章目录 一、实验框架图二、实验过程说明三、实验演示过程1、在A账号中创建S3存储桶2、在A账号创建S3存储桶访问策略3、在A账号创建信任开发账号的角色4、在B账号为用户添加内联策略5、在B账号中切换角色&#xff0c;以访问A账号中的S3资源 四、实验总结 一、实验框架图 本次…

dash--项目的前端展示简单基础

1.前置工作 创建虚拟环境&#xff1a; sudo apt-get install python3-venv # 安装 python3 -m venv venv # 在本目录下创建venv虚拟环境&#xff08;也是一个文件夹。如果用不到这个虚拟环境以后就rm -rf venv&#xff09; source venv/bin/activate # 激活虚拟环境临时使用清华…

C语言实现贪吃蛇小游戏

#include <stdio.h> #include <easyx.h> #include <iostream> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h> #define PI 3.14 #define NODE_WIDTH 40 //绘制蛇的节点 typedef struct {int x;int y;…

vue中electron与vue通信(fs.existsSync is not a function解决方案)

electron向vue发送消息 dist/main.js (整个文件配置在另一条博客里) win new BrowserWindow({width:1920,height:1080,webPreferences: {// 是否启用Node integrationnodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false// 是否在独立 JavaScript 环境中运行…

三.RocketMQ单机安装及集群搭建

RocketMQ单机安装及集群搭建 一&#xff1a;安装环境1.软硬件要求2.下载RocketMQ 二.安装单机MQ1.上传并解压2.目录介绍3.修改MQ启动时初始JVM内存4.启动NameServer与Broker5.测试RocketMQ 三.RocketMQ集群搭建1.集群概念特点2.集群模式分类3.集群工作流程4.双主双从集群搭建4.…

GORM:在Go中轻松管理数据库

GORM综合介绍 - Go对象关系映射库 在现代软件开发中&#xff0c;高效的数据库管理对于构建强大的应用程序至关重要。GORM是Go开发人员寻求与数据库进行交互的简化方式的宝贵工具。GORM是Go对象关系映射的缩写&#xff0c;它为Go的面向对象世界与数据库的关系世界之间提供了桥梁…

小红书协议算法最新版

如果您想通过学习来了解小红书的点赞、关注、私信等功能的实现&#xff0c;以下是一些一般性的思路和示例代码&#xff0c;供您学习参考&#xff1a; 1. 点赞功能&#xff1a; - 后端实现&#xff1a;在后端&#xff0c;您可以创建一个用于存储用户点赞信息的数据库表。对于每…

测试遇到bug怎么分析,这可能是我看到最专业的总结了!

为什么定位问题如此重要&#xff1f; 可以明确一个问题是不是真的“bug” 很多时候&#xff0c;我们找到了问题的原因&#xff0c;结果发现这根本不是bug。原因明确&#xff0c;误报就会降低多个系统交互&#xff0c;可以明确指出是哪个系统的缺陷&#xff0c;防止“踢皮球”&…

Qlik Sense Enterprise 忘记PostgreSQL密码

在 Windows 上安装 Qlik Sense Enterprise 期间会提供密码。如果您忘记了该密码&#xff0c;则无法找回&#xff1b;但是&#xff0c;可以按照以下步骤重置密码。 如何在 Qlik 中重置忘记的 PostgreSQL 密码... - Qlik Community - 1712725 如果该过程完成后记录了密码错误的…

Python开发技能实战-通过配置的代理服务器在具有外网连接的环境中在Pycharm中运行python代码

实现功能 打开科学上网工具&#xff0c;使得能够在浏览器科学上网&#xff0c;通过科学上网工具的配置文件&#xff0c;可以看出本地和远程代理的映射关系&#xff0c;此时&#xff0c;远程地址本地地址&#xff0c;远程端口本地端口。 1、在程序中配置请求网页代理请求。不需…