MFC网络通信-Udp服务端

news2024/11/29 1:42:09

目录

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode刷题详解——Pow(x, n)

1. 题目链接&#xff1a;50. Pow(x, n) 2. 题目描述&#xff1a; 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a;…

Mozilla Firefox 119 现已可供下载

Mozilla Firefox 119 开源网络浏览器现在可以下载了&#xff0c;是时候先看看它的新功能和改进了。 Firefox 119 改进了 Firefox View 功能&#xff0c;现在可以提供更多内容&#xff0c;如最近关闭的标签页和浏览历史&#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-…

YOLOv5 分类模型的加载

YOLOv5 分类模型的加载 flyfish 版本 6.2 yolov5s分类模型 python classify/train.py --model resnet18.pt --data cifar100 --epochs 5 --img 224resnet18模型 python classify/train.py --model resnet18.pt --data cifar100 --epochs 5 --img 128导出模型看一下结构 p…

基于SSM的理发店管理系统

基于SSM的理发店管理系统的设计与实现~ 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 主页 公告信息 管理员界面 用户界面 摘要 基于SSM&#xff08;Spring、Spring MVC、…

水库大坝可视化智能远程监管方案,助力安全监测智能巡检

一、背景需求 水库大坝作为防洪度汛的重要设施&#xff0c;其安全问题直接关系到人民群众的生命财产安全。因此&#xff0c;必须加强对大坝水库的安全管理&#xff0c;对水库除险加固和运行管护要消除存量隐患&#xff0c;实现常态化管理&#xff0c;同时要配套完善重点小型水…

windows使用FindWindow函数查找窗口句柄

理解什么是句柄&#xff1f; 对于“句柄”&#xff0c;之前一直停留在一知半解的认识层面&#xff0c;也说不清具体概念&#xff0c;只知道它是一个标识符&#xff0c;用来标记对象或者说某个东西的。只知其名不知其意。目前学习windows编程&#xff0c;对“句柄”做一个完整的…

GPT与人类共生:解析AI助手的兴起

随着GPT模型的崭新应用&#xff0c;如百度的​1​和CSDN的​2​&#xff0c;以及AI助手的普及&#xff0c;人们开始讨论AI对就业市场和互联网公司的潜在影响。本文将探讨GPT和AI助手的共生关系&#xff0c;以及我们如何使用它们&#xff0c;以及使用的平台和动机。 GPT和AI助手…

【AI视野·今日Robot 机器人论文速览 第六十一期】Tue, 24 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 24 Oct 2023 Totally 50 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Robot Fine-Tuning Made Easy: Pre-Training Rewards and Policies for Autonomous Real-World Reinforcement Learning Autho…

常用编程语言排行与应用场景汇总(2023.10)

文章目录 编程语言排行一、Python二、C三、C四、Java五、C#六、JavaScript七、VB&#xff08;Visual Basic&#xff09;八、PHP九、SQL十、ASM&#xff08;Assembly Language&#xff09;十一、Go十二、Scratch十三、Delphi/Object Pascal十四、MATLAB十五、Swift十六、Fortran…

十九、类型信息(5)

动态代理 _代理_是基本的设计模式之一。一个对象封装真实对象&#xff0c;代替其提供其他或不同的操作—这些操作通常涉及到与“真实”对象的通信&#xff0c;因此代理通常充当中间对象。这是一个简单的示例&#xff0c;显示代理的结构&#xff1a; interface Interface {voi…

【Java 进阶篇】Java Web开发:实现验证码功能

在Web应用程序中&#xff0c;验证码&#xff08;CAPTCHA&#xff09;是一种常见的安全工具&#xff0c;用于验证用户是否为人类而不是机器。验证码通常以图像形式呈现&#xff0c;要求用户在登录或注册时输入正确的字符。在这篇文章中&#xff0c;我们将详细介绍如何在Java Web…

NEFU离散数学实验特别篇1-树和图

树相关概念 离散数学中&#xff0c;树是一种重要的数据结构&#xff0c;它是一种无向连通图&#xff0c;并且不存在环。下面是树的相关概念和公式&#xff1a; 1. 顶点数为n的树&#xff0c;边数为n-1。 2. 度数为k的树中有k个分支。 3. 一棵树中最多只有两个度数大于1的顶点&a…

京东API获得JD商品详情 item_get-获得JD商品详情

item_get-获得JD商品详情 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;获取key和secret接入secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_sear…

面经(面试经验)第一步,从自我介绍开始说起

看到一位同学讲自己的面试步骤和过程&#xff0c;我心有所感&#xff0c;故此想整理下面试的准备工作。以便大家能顺利应对面试&#xff0c;通过面试... 求职应聘找工作&#xff0c;面试是必然的关卡&#xff0c;如今竞争激烈呀&#xff0c;想要得到自己喜欢的工作&#xff0c…

U盘装系统Win10步骤和详细教程

如果我们遇到系统问题&#xff0c;导致无法正常使用系统了&#xff0c;这时候就可以考虑给电脑重新安装系统。有用户想用U盘来进行系统的重装&#xff0c;但不清楚具体重装步骤&#xff0c;下面小编给大家详细介绍关于U盘重装系统Win10的详细步骤方法&#xff0c;帮助大家简单轻…

请求转发和重定向的区别

目录 一、请求转发 二、重定向 一、请求转发 request.getRequestDispatcher(页面).forwad(request,response) 请求转发&#xff1a; 客户端&#xff08;浏览器&#xff09;向服务器的资源 A 发起一次请求 ①。服务器的资源 A 接收到该请求后&#xff0c;将该请求转发到内部的…

FedAT:异步更新联邦学习方法

文章链接&#xff1a;FedAT: A Communication-Efficient Federated Learning Method with Asynchronous Tiers under Non-IID Data 发表会议: SC’21 (International Conference for High Performance Computing, Networking, Storage, and Analysis) 高性能计算&#xff0c;体…

高能有料 | 第二届OpenHarmony技术大会议程速递

第二届开放原子开源基金会OpenHarmony技术大会如约而至 让我们一起 开封无限惊喜的技术成果 开放无限前沿的议题干货 开启无限可能的未来之门 点击此处报名参会&#xff01;

接口自动化测试设计!

一、接口测试基础 1.什么是接口测试&#xff1f; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。接口测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖…