(一)和本文有关的博客
八大字符转换方式可参考文章: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)更进版本(见一、三):结构体一字节对齐
继承关系: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;
}
}
服务端,发送数据
//udpserver.h
#include
#include
#include
#include
#include
#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
#include
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()<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
#include
#include
#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
#include
#include
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()<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()<iNumber;
qDebug()<arrchResult;
qDebug()<arrchCode;
qDebug()<bOutLimit_Flag;
qDebug()<iMark;
qDebug()<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));