UDP传送和接受结构体结构的消息--Qt

2023-11-18

前言

    最近的项目用到UDP接收结构体,以为和普通的传送字符串的一样,没想到我还是太天真。要能够接收或者传送结构体,一个很重要的知识点是:结构体字节对齐。废话不多说,小课堂开始了!

结构体对齐

参考https://www.cnblogs.com/codingnutter/p/5634482.html

    许多计算机系统对基本数据类型合法的进行了一些限制,要求某种类型对象的地址必须是某个值(通常是2,4 和8)的倍数。这种对齐简化了形成处理器与存储系统之间的接口的硬件设计。当数据结构为结构体时,为了满足这种数据对齐的机制,编译器可能需要在结构体的字段的分配中插入间隙--向结构体中最大的元素对齐。

typedef struct   
{  
    char c;  
    int i[2];  
    double v;  
}S;  

    看上面的结构体,在没有数据对齐的情况下,size()=1+4*2+8=17字节。

    再编译器数据对齐处理后,他的结构大小变成了24字节(向结构体中最大的元素对齐),内存布局为

这个就是为什么我强转后,结构体中的部分数据是乱码的。

 改进后(结构体一字节对齐)

在听取网友意见后,发现将结构体一字节对齐后,操作真的是非常简单。

结构体一字节对齐:

#pragma pack(1)  //指定一字节对齐
struct Test_data{
    int iNumber;
    char arrchResult[45];
    char arrchCode[12];
    bool bOutLimit_Flag;
    int iMark;
    BYTE byteResultType;
};
#pragma pack()  //取消指定对齐,恢复缺省对齐

直接发送或接收

    data.iNumber=1;
    strcpy(data.arrchResult,"hello");
    strcpy(data.arrchCode,"0x29");
    data.bOutLimit_Flag=true;
    data.iMark=200;
    data.byteResultType=23;
    //发送
    udpSocket->writeDatagram((char *)&data,sizeof(data),QHostAddress::Broadcast,port);
     Test_data datagram; 
     //接收
     udpSocket->readDatagram((char*)&datagram,sizeof(datagram));

UDP发送和接收结构体消息(最原始的做法)

知道结构体的数据对齐的处理后,我们知道不能进行强转了,那么该怎么做呢?

我的做法是:将struct中的元素按字节大小一个个的存放到QByteArray中,QByteArray是连续的,接收时按大小再取出来就可以了。

直接上代码:

服务端(发送数据)

//udpserver.h
#include <QWidget>
#include<QVBoxLayout>
#include<QtNetwork/QUdpSocket>
#include<QTimer>
#include<QPushButton>

#define BYTE unsigned char
struct Test_data{
    int iNumber;
    char arrchResult[45];
    char arrchCode[12];
    bool bOutLimit_Flag;
    int iMark;
    BYTE byteResultType;
};


class UdpServer : public QWidget
{
    Q_OBJECT

public:
    UdpServer(QWidget *parent = 0);

public slots:
    void StartBtnClicked();
    void timeout();

private:
    QPushButton *startBtn;
    QVBoxLayout *mainLayout;
    int port;
    bool isStarted;
    QUdpSocket *udpSocket;
    QTimer *timer;

    QByteArray m_byteArray;
};

#endif // UDPSERVER_H
//udepserver.cpp
#include "udpserver.h"
#include<QtNetwork/QHostAddress>

#include<QDebug>

UdpServer::UdpServer(QWidget *parent)
    : QWidget(parent)
{
    //初始化
    Test_data data;
    data.iNumber=1;
    strcpy(data.arrchResult,"hello");
    strcpy(data.arrchCode,"0x29");
    data.bOutLimit_Flag=true;
    data.iMark=200;
    data.byteResultType=23;

    qDebug()<<sizeof(data);

    //结构体转byteArray
    byteArray.append((char *)&data.iNumber,sizeof(data.iNumber));
    byteArray.append(data.arrchResult,sizeof(data.arrchResult));
    byteArray.append(data.arrchCode,sizeof(data.arrchCode));
    byteArray.append((char *)&data.bOutLimit_Flag,sizeof(data.bOutLimit_Flag));
    byteArray.append((char *)&data.iMark,sizeof(data.iMark));
    byteArray.append((char *)&data.byteResultType,sizeof(data.byteResultType));

    setWindowTitle(tr("UDP Server"));

    startBtn=new QPushButton(tr("start"),this);

    mainLayout=new QVBoxLayout(this);
    mainLayout->addWidget(startBtn);

    connect(startBtn,SIGNAL(clicked(bool)),this,SLOT(StartBtnClicked()));

    port=5555;
    isStarted=false;
    udpSocket=new QUdpSocket(this);
    timer=new QTimer(this);
    connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));

}


void UdpServer::StartBtnClicked()
{
    if(!isStarted)
    {
        startBtn->setText(tr("stop"));
        timer->start(1000);
        isStarted=true;
    }
    else
    {
        startBtn->setText(tr("start"));
        isStarted=false;
        timer->stop();
    }

}

void UdpServer::timeout()
{
    udpSocket->writeDatagram(byteArray.data(),byteArray.size(),QHostAddress::Broadcast,port);
}

客户端接收数据

#ifndef UDPCLIENT_H
#define UDPCLIENT_H

#include <QWidget>
#include<QtNetwork/QUdpSocket>
#include<QByteArray>

#define BYTE unsigned char
struct Test_data{
    int iNumber;
    char arrchResult[45];
    char arrchCode[12];
    bool bOutLimit_Flag;
    int iMark;
    BYTE byteResultType;
};

class UdpClient : public QWidget
{
    Q_OBJECT

public:
    UdpClient(QWidget *parent = 0);

public slots:
    void dataReceived();

private:
    int port;
    QUdpSocket *udpSocket;


};

#endif // UDPCLIENT_H

#include "udpclient.h"
#include<QMessageBox>
#include<QtNetwork/QHostAddress>
#include<QDebug>

UdpClient::UdpClient(QWidget *parent)
    : QWidget(parent)
{
    setWindowTitle(tr("UDP Client"));
    port=5555;

    udpSocket=new  QUdpSocket(this);
    connect(udpSocket,SIGNAL(readyRead()),this,SLOT(dataReceived()));

    bool result=udpSocket->bind(port);  //绑定端口
    if(!result)
    {
        QMessageBox::information(this,tr("error"),tr("udp socket create error"));
        return;
    }


}

void UdpClient::dataReceived()
{
    while(udpSocket->hasPendingDatagrams())  //有未处理的报文
    {
        Test_data* datagram=new Test_data; //用于存放接收的数据
        QByteArray recvMsg;
        qDebug()<<udpSocket->pendingDatagramSize();
        recvMsg.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(recvMsg.data(),recvMsg.size());

        int pos=0;
        memcpy(&datagram->iNumber,recvMsg.constData(),sizeof(datagram->iNumber));
        pos+=sizeof(datagram->iNumber);
        memcpy(datagram->arrchResult,recvMsg.constData()+pos,sizeof(datagram->arrchResult));
        pos+=sizeof(datagram->arrchResult);
        memcpy(datagram->arrchCode,recvMsg.constData()+pos,sizeof(datagram->arrchCode));
        pos+=sizeof(datagram->arrchCode);
        memcpy(&datagram->bOutLimit_Flag,recvMsg.constData()+pos,sizeof(datagram->bOutLimit_Flag));
        pos+=sizeof(datagram->bOutLimit_Flag);
        memcpy(&datagram->iMark,recvMsg.constData()+pos,sizeof(datagram->iMark));
        pos+=sizeof(datagram->iMark);
        memcpy(&datagram->byteResultType,recvMsg.constData()+pos,sizeof(datagram->byteResultType));


        qDebug()<<datagram->iNumber;
        qDebug()<<datagram->arrchResult;
        qDebug()<<datagram->arrchCode;
        qDebug()<<datagram->bOutLimit_Flag;
        qDebug()<<datagram->iMark;
        qDebug()<<datagram->byteResultType;

    }
}

结束语

在恶作剧的心情过后,我把改进后的方法提前了 。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UDP传送和接受结构体结构的消息--Qt 的相关文章

  • 必须打开存储才能执行此操作 - System.IO.Packaging.Package

    我正在使用 System IO Packaing Package 类来压缩文件 我的应用程序的多个实例可以同时运行 并读取和保存文件 当处理小文件时 一切似乎都很好 但是当涉及大文件时 如果应用程序的两个实例同时保存 我会收到一个异常 消息
  • 检查列表是否包含另一个列表。 C#

    编辑 只是说 ContainsAllItem 中的注释解释得最好 很抱歉问这个问题 我知道以前有人问过这个问题 但我只是不明白 好的 所以我想检查一个列表是否包含另一个列表中的所有项目WITHOUT重叠 以及根据类字符串 名称变量 称为项目
  • 使用 QWT 构建时出错

    我收到一个错误 undefined reference to QwtPlot QwtPlot QWidget 当我尝试构建我的项目时 即使设置中一切看起来都很好 在我的 CmakeLists txt 中我有 include director
  • 如何在 Windows 窗体中运行屏幕保护程序作为其背景?

    如何在 Windows 窗体中运行屏幕保护程序作为其背景 用户还可以在屏幕保护程序运行时与表单控件进行交互 为什么这个 我们有一个案例 需要在用户时运行 Windows Bubbles 屏幕保护程序 可以继续与表单控件交互吗 您可以使用以下
  • 将 C# 反射代码移植到 Metro-Ui

    我正在尝试移植使用反射的现有 C 类 通用工厂 但我无法编译这段代码 Type types Assembly GetAssembly typeof TProduct GetTypes foreach Type type in types i
  • C 中的模仿函数重写

    具体来说 函数重写能够调用基本重写方法 这有两部分 一个是预编译的库代码 1 另一个是库的用户代码 2 我在这里实现了一个尽可能最小的经典 Person 和 Employee 示例 非常感谢了解 OOP 概念的铁杆 C 开发人员的回应 我正
  • 使用反射获取基类的受保护属性值

    I would like to know if it is possible to access the value of the ConfigurationId property which is located in the base
  • .net Framework (.net 4.0) 中定义 Base 3 数字的类

    我正在寻找一些可以用来定义 3 基数 三进制数 的类 有什么我可以在 net 框架中使用的东西或者我需要写一些东西吗 谢谢你的帮助 您可以使用解析Convert ToInt32 s base http msdn microsoft com
  • 在“using”语句中使用各种类型 (C#)

    自从C usingstatements只是try finally dispose 的语法糖 为什么它接受多个对象仅当它们属于同一类型时 我不明白 因为它们需要的只是 IDisposable 如果它们都实现 IDisposable 应该没问题
  • 使用scanf()时如何区分整数和字符

    我只是使用该功能scanf 代码如下 scanf d a printf d a 当我输入1时 它会像我想要的那样打印1 但即使我输入 1a 它也会像以前一样打印 1 当用户输入非整数时 例如 2 3 12ab 1 a 我想向用户显示 输入整
  • 为什么WCF中不允许方法重载?

    假设这是一个ServiceContract ServiceContract public interface MyService OperationContract int Sum int x int y OperationContract
  • 如何不在类中实现接口的功能?

    面试时面试官问了我以下问题 但我不知道这个问题的答案是什么 请帮忙 如果我不想 我必须做什么 在我的类中实现一个函数 在接口中声明为 由我班实施 Edited 我正在使用 NET 和 C 如果有人可以提供 C 示例代码示例 那就太好了 Th
  • 微软语音识别速度

    我正在使用微软的语音识别器开发一个小型练习应用程序 对于我正在做的事情来说 我似乎无法让它足够快地识别单个单词 我希望能够正常说话 系统将从我所说的内容中抓取 关键字 并生成一个字符串 目前我正在使用 5 个单词的自定义语法 红 蓝 黄 绿
  • 你能解释一下这个C++删除问题吗?

    我有以下代码 std string F WideString ws GetMyWideString std string ret StringUtils ConvertWideStringToUTF8 ws ret return ret W
  • 为什么C语言中可以使用多个分号?

    在 C 中我可以执行以下操作 int main printf HELLO WORLD 它有效 这是为什么 我个人的想法 分号是一个 NO OPERATION 来自维基百科 指示符 拥有一大串分号与拥有一个分号并告诉 C 语句已结束具有相同的
  • 在 SQL Server 上执行分页的最佳方式是什么?

    我有一个数据库超过200万记录 我需要执行分页以在我的 Web 应用程序上显示 该应用程序每页必须有 10 条记录DataGrid 我已经尝试使用ROW NUMBER 但是这种方式会选择所有 200 万条记录 然后只得到 10 条记录 我也
  • 将一个 long 转换为两个 int 以进行重构

    我需要将一个参数作为两个 int 参数传递给 Telerik Report 因为它不能接受长参数 将 long 拆分为两个 int 并在不丢失数据的情况下重建它的最简单方法是什么 使用掩蔽和移位是最好的选择 根据文档 long 保证为 64
  • 如何强制执行特定的 UserControl 设计

    我正在编写一个基本用户控件 它将由一堆其他用户控件继承 我需要对所有这些后代控件强制执行某种设计 例如 顶部必须有几个按钮以及一个或两个标签 后代用户控件区域的其余部分可以自由放置任何内容 最初 我认为我可以将一个面板放到 Base Use
  • 如何使用 ASP.NET Web 表单从代码隐藏中访问更新面板内的文本框、标签

    我在更新面板中定义了一些控件 它们绑定到中继器控件 我需要根据匿名字段隐藏和显示用户名和国家 地区 但问题是我无法以编程方式访问更新面板中定义的控件 我如何访问这些控件 我也在网上查找但找不到很多参考资料 下面是来自aspx页面和 cs页面
  • 创建带有部分的选项卡式侧边栏 WPF

    我正在尝试创建一个带有部分的选项卡式侧边栏 如 WPF 中的以下内容 我考虑过几种方法 但是有没有更简单 更优雅的方法呢 方法一 列表框 Using a ListBox并将 SelectedItem 绑定到右侧内容控件所绑定的值 为了区分标

随机推荐