Qt总结之十三:QUDPSocket详解

前言

(一)和本文有关的博客

八大字符转换方式可参考文章:https://blog.csdn.net/Aidam_Bo/article/details/84111776

uint8_t / uint16_t / uint32_t /uint64_t数据类型详解:https://blog.csdn.net/Aidam_Bo/article/details/85775846

QByteArray详解:https://blog.csdn.net/Aidam_Bo/article/details/85778012

字节对齐:https://blog.csdn.net/Aidam_Bo/article/details/85211303

(二)字节对齐

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

typedef struct CStudent
{  
    char firstName[16];
    char lastName[16];  
    int age;  
    double height;  
}TAGSTUDENT;  


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

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

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

这个时候该上课了:https://blog.csdn.net/Aidam_Bo/article/details/85211303  小雷锋同志来了~!~

UDP发送和接收结构体消息

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

(1)原始示例版本(见二):将struct中的元素按字节大小一个个的存放到QByteArray中,QByteArray是连续的,接收时按大小再取出来
(2)更进版本(见一、三):结构体一字节对齐 

一、Qt UDP 传输用到的数据转换

继承关系:QUDPSocket-->QAbstructSocket

我们先看UDP发送数据和接收数据的两个接口函数

(1)写入

官方写入接口给了两个:

qint64    writeDatagram(const char * data, qint64 size, const QHostAddress & address, quint16 port)
qint64    writeDatagram(const QByteArray & datagram, const QHostAddress & host, quint16 port)

 这里是做的一个文件管理系统中的前台通知后台删除文件的UDP协议功能

//发送删除文件
typedef struct T_Protocol_File_Del
{
	uint16_t frame_head;   //2
	uint16_t len;          //2
	uint8_t cmd_type;      //1
	uint8_t del_flag;      //1
	char delFile[80];      //80
	uint32_t frame_tail;//[命令保留位]   4


};

void fileReceive::toDelFileItem()
{
	//发送指令9,删除文件信息
	fileDelProtocol.frame_head = 0xEB90;
	fileDelProtocol.len = sizeof(T_Protocol_File_Del);
	fileDelProtocol.cmd_type = 9;
	strcpy(fileDelProtocol.delFile, fileNmae.toLocal8Bit().data());
	fileDelProtocol.frame_tail = 0x55aa55aa;
	m_udpSocket->writeDatagram((char*)&fileDelProtocol, sizeof(fileDelProtocol), QHostAddress("192.168.10.10"), 8888);//(char*)&fileProtocol, sizeof(fileProtocol)

	m_message.close();
}

 

(2)读取

读取接口给了一个:

qint64    readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0)

 接收删除文件

void fileSend::dataReceived()
{
	const int headLen = sizeof(fileDelProtocol.frame_head);     //包头长度
	const int lenLen = sizeof(fileDelProtocol.len);
	const int cmdLen = sizeof(fileDelProtocol.cmd_type);
	const int flagLen = sizeof(fileDelProtocol.del_flag);
	const int fileLen = sizeof(fileDelProtocol.delFile);
	const int tailLen = sizeof(fileDelProtocol.frame_tail);       //包尾长度

	QByteArray datagram;
	datagram.resize(udpSocket->pendingDatagramSize());
	udpSocket->readDatagram(datagram.data(), datagram.size());
	char *rData = datagram.data();

	memcpy(&fileDelProtocol.frame_head, rData, headLen); //取包头
	memcpy(&fileDelProtocol.len, rData + headLen, lenLen); //长度
	memcpy(&fileDelProtocol.cmd_type, rData + headLen + lenLen, cmdLen);//取类型
	memcpy(&fileDelProtocol.del_flag, rData + headLen + lenLen + cmdLen, flagLen);
	memcpy(&fileDelProtocol.delFile, rData + headLen + lenLen + cmdLen+flagLen , fileLen);//取删除文件名


	m_delFile = QString::fromLocal8Bit(fileDelProtocol.delFile);
	
	switch (fileDelProtocol.cmd_type)
	{
	case 8://发送文件信息
		emit sigSendFile();
		break;
	case 9://删除文件
		ui.lineEdit->setText(m_delFile);
		//遍历所有磁盘
		foreach(QFileInfo info, QDir::drives())
		{
			QString strDir(info.absoluteFilePath()+m_delFile);
			QFile file(strDir);
			file.remove();
		}
		//判断文件是否删除//  发送ACK
		emit sigSendACK();
		ui.lineEdit_2->setText(m_delFile + QString::fromLocal8Bit("文件已删除"));
		break;
	default:
		break;
	}

}

二、原版Demo示例(结构体没有一字节对齐)

 

服务端,发送数据

//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;
 
    }
}

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

将结构体一字节对齐后,操作so easy ~!~

结构体一字节对齐:

#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));


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值