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

前言

    最近的项目用到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;

    }
}

结束语

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

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值