一、弹性结构体
二、通讯协议设计
设计数据协议单元的数据结构
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
typedef unsigned int uint;
struct PDU
{
uint uiPDULen; //总的协议数据单元大小
uint uiMsgType; //消息类型
char caData[64]; //消息数据
uint uiMsgLen; //实际消息的长度
int caMsg[]; //实际消息
};
PDU* mkPDU(uint uiMsgLen); //创建协议数据单元
通过动态申请实际消息的大小,通过访问结构体最后的数组的地址来访问消息。
#include"protocol.h"
PDU *mkPDU(uint uiMsgLen)
{
uint uiPDULen = sizeof(PDU) + uiMsgLen;
PDU* pdu = (PDU*)malloc(uiPDULen);
if(NULL == pdu)
{
exit(EXIT_FAILURE);
}
memset(pdu,0,uiPDULen); // 将pdu所指向的内存空间清0
pdu->uiPDULen = uiPDULen;
pdu->uiMsgLen = uiMsgLen;
return pdu;
}
三、数据收发测试
测试数据的接收
void TcpClient::on_send_btn_clicked()
{
QString strMsg = ui->lineEdit->text();
if(!strMsg.isEmpty()){
PDU* pdu = mkPUD(strMsg.size());
pdu->uiMsgType = 8888;
memecpy(pdu->caData,strMsg.toStdString().c_str(),strMsg.size());
m_tcpSocket.write((char*)pdu , pdu->uiPDULen);
free(pdu);
pdu = NULL;
}
else{
QMessageBox::warning(this,"信息发送","发送数据不能为空!");
}
}
通过测试发现通过拷贝后的数据如果是中文,则会导致出现乱码,
解决方法如下:
使用memcpy
将QString类型的消息内容复制到了PDU
结构体中的caData
成员。但是,memcpy
仅适用于二进制数据的复制,对于包含字符串的QString,可能需要考虑字符串编码的问题。
由于输出的数据可能包含中文字符,建议使用QString的toUtf8
函数来获取UTF-8编码的字节数
组,然后使用memcpy
进行复制。
void TcpClient::on_send_btn_clicked()
{
QString strMsg = ui->lineEdit->text();
if (!strMsg.isEmpty()) {
QByteArray utf8Data = strMsg.toUtf8();
PDU* pdu = mkPDU(utf8Data.size());
pdu->uiMsgType = 8888;
memcpy(pdu->caData, utf8Data.constData(), utf8Data.size());
m_tcpSocket.write((char*)pdu, pdu->uiPDULen);
qDebug() << "send msg data:" << pdu->caData;
free(pdu);
pdu = NULL;
} else {
QMessageBox::warning(this, "信息发送", "发送数据不能为空!");
}
qDebug() << "send msg:" << strMsg;
}
通过测试,客户端这边显示正常
再对服务器进行测试
这里单独设计了一个类mytcpsocket,专门用于接收消息
#ifndef MYTCPSOCKET_H
#define MYTCPSOCKET_H
#include <QTcpSocket>
class MyTcpSocket : public QTcpSocket
{
Q_OBJECT
public:
explicit MyTcpSocket(QObject *parent = nullptr);
public slots:
void recvMsg();
};
#endif // MYTCPSOCKET_H
#include "mytcpsocket.h"
#include<QDebug>
#include"protocol.h"
MyTcpSocket::MyTcpSocket(QObject *parent)
: QTcpSocket{parent}
{
connect(this,&MyTcpSocket::readyRead
,this,&MyTcpSocket::recvMsg);
}
void MyTcpSocket::recvMsg()
{
qDebug()<<this->bytesAvailable();
uint uiPDULen = 0;
this->read((char*)&uiPDULen,sizeof(uint));
uint uiMsgLen = uiPDULen - sizeof(PDU);
PDU* pdu = mkPDU(uiMsgLen);
this->read((char*)pdu+sizeof(uint),uiPDULen-sizeof(uint));
qDebug()<<pdu->uiMsgType<<(char*)pdu->caData;
}
每次接收消息后,都会有一个单独的tcpsocket类来管理,并添加到列表中
void MyTcpServer::incomingConnection(qintptr socketDescriptor)
{
qDebug()<<"new client connected!";
MyTcpSocket* pTcpSocket = new MyTcpSocket;
pTcpSocket->setSocketDescriptor(socketDescriptor);
m_tcpSocketList.append(pTcpSocket);
}
启动客户端向服务器发送消息进行测试,测试结果正常
至此通讯协议设计完成,客户端和服务器已经可以正常通信了