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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode算法题解|​ 669. 修剪二叉搜索树​、108. 将有序数组转换为二叉搜索树、​538. 把二叉搜索树转换为累加树​

一、LeetCode 669. 修剪二叉搜索树​ 题目链接&#xff1a;669. 修剪二叉搜索树 题目描述&#xff1a; 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变…

042-第三代软件开发-485通信

第三代软件开发-485通信 文章目录 第三代软件开发-485通信项目介绍485通信RS-485 简介RS-232 简介RS-485 与 RS-232 区别Qt 中使用485 总结一下 关键字&#xff1a; Qt、 Qml、 QSerialPort、 QSerialPort、 QThread 项目介绍 欢迎来到我们的 QML & C 项目&#xff01…

J2EE项目部署与发布(Linux版本)->jdktomcat安装,MySQL安装,后端接口部署,linux单体项目前端部署

jdk&tomcat安装MySQL安装后端接口部署linux单体项目前端部署 1.jdk&tomcat安装 上传jdk、tomcat安装包 解压两个工具包 #解压tomcat tar -zxvf apache-tomcat-8.5.20.tar.gz #解压jdk tar -zxvf jdk-8u151-linux-x64.tar.gz 配置并且测试jdk安装 #配置环境变量 vim /e…

【tensorboard打开失败】No dashboards are active for the current data set.

这里我再跟视频学的时候&#xff0c;找了很多的指令&#xff0c;说是对应版本不一样&#xff0c;但是发现用了很多指令都可以弹出来跳转的url&#xff0c;那应该就不是输入指令的问题 直到我想把logs里面的文件删掉重新跑的时候&#xff0c;我突然注意到这里有中文字符&#xf…

Hadoop HDFS(分布式文件系统)

一、Hadoop HDFS(分布式文件系统) 为什么要分布式存储数据 假设一个文件有100tb&#xff0c;我们就把文件划分为多个部分&#xff0c;放入到多个服务器 靠数量取胜&#xff0c;多台服务器组合&#xff0c;才能Hold住 数据量太大&#xff0c;单机存储能力有上限&#xff0c;需要…

大数据Doris(十五):Doris表的字段类型

文章目录 Doris表的字段类型 一、TINYINT数据类型 二、SMALLINT数据类型 三、INT数据类型

89 柱状图中最大的矩形

柱状图中最大的矩形 类似接雨水&#xff08;反过来&#xff0c;相当于找接雨水最少的一段&#xff09;题解1 暴力搜索&#xff08;超时&#xff09; O ( N 2 ) O(N^2) O(N2)另一种 题解2 单调栈【重点学习】常数优化 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的…

自动驾驶数据标注有哪些?

自动驾驶汽车&#xff1a;人工智能(AI)的焦点 我们在上篇中谈到&#xff0c;人工智能驱动汽车解决方案的市场规模预计到 2025年将增长十倍以上&#xff0c;提升车内体验的商机领域以及 AI 模型的无偏见训练数据的重要性。在本篇中&#xff0c;我们将介绍车外体验的关键组成部分…

何恺明:在cuhk解答科研问题

文章目录 1. 大模型的未来:数据效益是个问题2. 未来三年研究重点:视觉自监督学习3. 选择课题的标准:好奇心和热情4. AI将成为几乎所有事情的基础工具5. 用疑问解答AI模型可解释性问题AcknowledgementReference何恺明最近在香港中文大学参加一个讲座过程中所述: 1. 大模型的…

Django中的FBV和CBV

一、两者的区别 1、在我们日常学习Django中&#xff0c;都是用的FBV&#xff08;function base views&#xff09;方式&#xff0c;就是在视图中用函数处理各种请求。而CBV&#xff08;class base view&#xff09;则是通过类来处理请求。 2、Python是一个面向对象的编程语言…

idea:解决jsp request.getParameter爆红的问题

文章目录 1. 复现错误2. 分析问题3. 解决问题1. 复现错误 今天在写jsp代码时,出现如下错误: 2. 分析问题 这是没有引入相关jsp的相关jar包引起的。 我们可按如下步骤,引入jsp的相关jar包。 3. 解决问题 File -> Project Structure -> Modules -> Dependences -&g…

创造产业链协同优势后,凌雄科技在DaaS行业转动成长飞轮

企业服务领域&#xff0c;一直存在一种共识&#xff1a;做好很难&#xff0c;但一旦服务模式跑通了&#xff0c;得到了市场的认可&#xff0c;要滚起雪球就会事半功倍。 重资产、重运营的DaaS&#xff08;设备及服务&#xff09;赛道&#xff0c;是个非常典型的细分领域。在这…

wireshark捕获DNS

DNS解析&#xff1a; 过滤项输入dns&#xff1a; dns查询报文 应答报文&#xff1a; 事务id相同&#xff0c;flag里 QR字段1&#xff0c;表示响应&#xff0c;answers rrs变成了2. 并且响应报文多了Answers 再具体一点&#xff0c;得到解析出的ip地址&#xff08;最底下的add…

react条件渲染

目录 前言 1. 使用if语句 2. 使用三元表达式 3. 使用逻辑与操作符 列表渲染 最佳实践和注意事项 1. 使用合适的条件判断 2. 提取重复的逻辑 3. 使用适当的key属性 总结 前言 在React中&#xff0c;条件渲染指的是根据某个条件来决定是否渲染特定的组件或元素。这在构…

2023SHCTF web方向wp

1.ezphp 看一眼&#xff0c;你大爷&#xff0c;啥玩意都给我过滤完了。 还好下面有preg_replace()/e&#xff0c;会把replacement当作php语句执行 传参pattern.*&#xff0c; .*表示任意字符&#xff0c;code{${phpinfo()}} &#xff0c;为什么这样写&#xff0c;因为,print_…

海上风电应急救援vr模拟安全培训提高企业风险防范能力

相比传统的发电厂&#xff0c;海上风电作业积累的经验少&#xff0c;风险高&#xff0c;因此为了规范施工人员的行为和操作&#xff0c;保障生产安全进行&#xff0c;开展海上风电VR安全培训具有重要意义。 有助于提高员工的安全意识 通过模拟真实的海上风电作业环境&#xff0…

nn.LayerNorm解释

这个是层归一化。我们输入一个参数&#xff0c;这个参数就必须与最后一个维度对应。但是我们也可以输入多个维度&#xff0c;但是必须从后向前对应。 import torch import torch.nn as nna torch.rand((100,5)) c nn.LayerNorm([5]) print(c(a).shape)a torch.rand((100,5,…

湖南互联网医院-让患者随时随地接受医疗服务

打造移动互联网医院&#xff0c;就是&#xff0c;通过移动互联网将医院与患者、医院内部&#xff08;医生、护士、领导层&#xff09;、医院与生态链上的各类组织机构连接起来。以患者为中心&#xff0c;优化医院业务流程&#xff0c;提升医疗服务质量与医院资源能效&#xff0…

【uniapp】html和css-20231031

我想用控件和样式来表达应该会更贴切&#xff0c;html和css的基础需要看看。 关于html&#xff1a;https://www.w3school.com.cn/html/html_layout.asp 关于css&#xff1a;https://www.w3school.com.cn/css/index.asp html让我们实现自己想要的布局&#xff08;按钮&#xff0…

【QT】基本的绘图操作和高级绘图

基本绘图 新建项目 重新绘图事件 画基本图形 #include "widget.h" #include "ui_widget.h" #include <QPainter>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }/…