发送的结构体包含帧头header(占两字节)、数据长度dataLength(占一字节)、数据my_data(不多于64字节)、校验和check_sum(前面所有数据所占字节和,本身只占一个字节)。
发送方的结构体:
这里要特别注意== #pragma pack(1) ==的使用,涉及到结构体内存对齐,使用这行可以设置结构体对齐方式为1字节,这点特别重要,我在这个坑里绕了好久才走出来!!这样设置主要是因为后面要使用到结构体的大小sizeof(senddata)。
#define DATA_LEN 64 #pragma pack(1) //设置结构体为1字节对齐 typedef struct sendData {
uchar header[2]; //帧头(2字节) uchar才能存十六进制数 uchar dataLength; //数据个数(1字节),小于64 char my_data[DATA_LEN]; //数据(小于64字节) uchar check_sum; //校验和(1字节) 前面所有字节累加和 }senddata; #pragma pack() //结束结构体对齐设置
注意:结构体中的数据存储最好不要用 char* 类型,在后面用到结构体强转、结构体转QByteArray数组时容易出错。转的时候可能只拷贝了char的地址,没有拷贝到数据;也有可能由于char数据长度不定,发送的时候出现问题;也有可能接收方收到解析的时候出现问题。
char data[64]; QString str="12fff"; QByteArray ba=str.toLatin1(); char *temp=ba.data(); memcpy(data,temp,ba.length());
sendData st_senddata; QByteArray get_data, sendTcpData; char *temp; QString str; //senddata.header.resize(2); st_senddata.header[0] = 0x55; //假设帧头就是0X55 0XAA st_senddata.header[1] = 0xAA; str = ui->textEdit_Send->toPlainText().toLocal8Bit(); //数据超长提醒 if(str.length() > 64) {
QMessageBox::information(this,tr("提示"),tr("数据长度限制为64!"),QMessageBox::Yes); ui->textEdit_Send->clear(); return; } //填好数据 get_data=(QByteArray)ui->textEdit_Send->toPlainText().toLocal8Bit(); //直接获取用户输入的同时将QString转成QByteArray temp=get_data.data(); //将QByteArray转成char* memcpy(st_senddata.my_data,temp,get_data.length()); //不拷贝内存传结构体时就只会传一个指针过去 //填好数据长度 st_senddata.dataLength = get_data.length(); //填好校验和,就是my_data长度+header两字节+datalength一字节 st_senddata.check_sum = get_data.length() + 3; //使用字节数组,将结构体转为字符数组,发送的是字符数组(数据在传输过程中都是byte类型的) //直接sizeof(senddata)内存会变小,设置了对齐方式解决,,,只给他赋予数据长度加帧头、校验和所占字节 sendTcpData.resize((get_data.length()+3)); //将封装好的结构体转为QByteArray数组,因为传输都是Byte类型 memcpy(sendTcpData.data(),&st_senddata,(get_data.length()+3)); /*因为数据长度可能没有占满64字节,校验和又是存在数据之后的,所以有一段内存可能是空的,因此此处手动 把校验和值添加在QByteArray数组最后,这样发送出去的数据就是连续的*/ sendTcpData.append(st_senddata.check_sum); //发送完整的QByteArray数组,包含所有结构体信息 socket->write(sendTcpData); ui->textEdit_Recv->insertPlainText("send:"+str+"\n"); socket->flush(); //释放socket缓存 ui->textEdit_Send->clear(); //发送出去后将发送文本清空 //释放指针、清空QByteArray数组 free(temp); temp = NULL; get_data.clear(); get_data.squeeze(); sendTcpData.clear(); sendTcpData.squeeze();
接收方的结构体:
#define DATA_LEN 64 #pragma pack(1) //接收数据的格式 typedef struct receiveData {
uchar header[2]; //帧头 uchar dataLength; //数据个数(1字节),小于64 char my_data[DATA_LEN]; //数据(小于64字节) uchar check_sum; //校验和(1字节) 前面所有字节累加和 }st_receivedata; #pragma pack()
receiveData st_receiveTcpData, *get_Data; QByteArray buffer; QString str; //读取缓冲区数据 buffer = socket->readAll(); if(!buffer.isEmpty()) {
memset(&st_receiveTcpData,0,sizeof (st_receiveTcpData)); get_Data = (receiveData*)buffer.data(); //强转为结构体,需要用结构体指针接收 //读取帧头 for(int i = 0; i < sizeof (st_receiveTcpData.header); i++) {
st_receiveTcpData.header[i] = get_Data->header[i]; } //读取数据长度 st_receiveTcpData.dataLength = get_Data->dataLength; //读取数据 for(int i = 0; i < buffer.length() - 4; i++)//buffer的长度减去header、datalength、check_sum的长度就是数据的长度 {
st_receiveTcpData.my_data[i] = get_Data->my_data[i]; } //读取校验和,因为发送的时候避免my_data有空内容,所以把校验和放在了my_data后面,所以此处只 //需要读取my_data后面的值就是校验和了。 get_Data->check_sum = get_Data->my_data[buffer.length()-4]; st_receiveTcpData.check_sum = get_Data->check_sum; //将my_data数据转为QString str = QString(QLatin1String(st_receiveTcpData.my_data)); //将my_data在文本框显示 ui->textEdit_Recv->insertPlainText("receive:"+str+"\n"); //释放内存 free(get_Data); buffer.clear(); buffer.squeeze(); }
总的来说,思路很简单,但是坑也很多,尤其是数据转换问题,稍不注意就出错。