以自定数据包格式进行通信
数据包格式如下:
服务端代码如下:
。cpp文件:主界面程序会发送emit sendTime()和emit sendOCRMsg(OCRMsg, dateTime)信号对应HeratPack和sendData槽函数
#include "TCPthread.h" #include <qdebug.h> #include <qtimer.h> #include <qthread.h> #include <qdatastream.h> TCPthread::TCPthread(QObject *parent) { qDebug() << "TCPthread线程id:" << QThread::currentThread(); tcpServer = NULL; //监听套接字,指定父对象,让其自动回收空间 tcpServer = new QTcpServer(this); tcpServer->listen(QHostAddress::AnyIPv4, 8888); //当有客服端连接时 connect(tcpServer, &QTcpServer::newConnection,this,&TCPthread::createSocket); //----------------------------------------------------------------------------------------------- } void TCPthread::createSocket() { //取出建立好连接的套接字 QTcpSocket *tcpSocket = tcpServer->nextPendingConnection(); socketList.push_back(tcpSocket); //获取客户端的IP和端口 QString ip = tcpSocket->peerAddress().toString(); qint16 port = tcpSocket->peerPort(); IPort = QString("TCPClient-[%1:%2]:成功连接").arg(ip).arg(port); qDebug() << IPort; connect(tcpSocket, &QTcpSocket::connected, [=]() { qDebug() << "成功和服务器建立好连接"; }); //接受客服端的数据 connect(tcpSocket, &QTcpSocket::readyRead,this, &TCPthread::readData); //当客户端断开连接 connect(tcpSocket, &QTcpSocket::disconnected, this, &TCPthread::clientDiscon); } //从客户端读取数据 void TCPthread::readData() { //从通信套接字中取出内容 if (socketList.size() != 0) { for (int i = 0; i < socketList.size(); i++) { QByteArray array = socketList[i]->readAll(); } } } //客户端断开连接 void TCPthread::clientDiscon() { //与服务器连接的客户端 QTcpSocket *tcpsocket = qobject_cast<QTcpSocket*>(QObject::sender()); //移除 socketList.removeOne(tcpsocket); qDebug() << "Client-IP:" << tcpsocket->peerAddress().toString() << "断开连接"; tcpsocket->deleteLater(); } //TCP服务器发送板坯报文数据槽函数 void TCPthread::sendData(QString OCR_Msg, QDateTime dateTime) { //封装包头 QByteArray sendOCRByte;//用于发送数据的字节数组 QDataStream out(&sendOCRByte, QIODevice::WriteOnly);//使用数据流写入数据 out.setByteOrder(QDataStream::LittleEndian);//设置小端模式 out << ushort(0) << m_OCRPackID;//占位符,这里必须要先这样占位,然后后续读算出整体长度后在插入 qDebug() << "报文头长度:" << sendOCRByte.length();//4 //封装数据 QString s_data = OCR_Msg + dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz") +"0"; qDebug() << s_data << "字符串长度:" << s_data.length();//48 QByteArray ba = s_data.toLocal8Bit(); //字符串转字节数组 qDebug() << "字符字节数组长度:" << ba.length(); //48 //--------------------------------------------------------- char * c_data; c_data = ba.data(); sendOCRByte.append(c_data);//在4个字节数组中添加48个字符串 //--------------------------------------------------------- out.device()->seek(0);//回到数据流开头,插入数据的长度 ushort t_len = (ushort)(sendOCRByte.length()); out << t_len; qDebug() << "板坯报文长度:" << t_len;//52 //发送数据|多例 if (socketList.size() != 0) { for (int i = 0; i < socketList.size(); i++) { socketList[i]->write(sendOCRByte); socketList[i]->flush(); } } } //发送心跳报文 void TCPthread::HeratPack() { if (socketList.size() != 0) { for (int i = 0; i < socketList.size(); i++) { //用于心跳报文要发送的数据 QByteArray sendHeartByte; //使用数据流写入数据 QDataStream out(&sendHeartByte, QIODevice::WriteOnly); //设置小端模式 out.setByteOrder(QDataStream::LittleEndian); //占位符,这里必须要先这样占位,然后后续读算出整体长度后在插入 out << ushort(0) << m_heartPackID ; //回到文件开头,插入数据的长度 out.device()->seek(0); ushort len = (ushort)(sendHeartByte.size()); out << len;//4 qDebug() <<"心跳报文长度:" << len << "心跳报文ID:" << m_heartPackID; //往套接字缓存中写入数据,并发送 socketList[i]->write(sendHeartByte); socketList[i]->flush(); } } } TCPthread::~TCPthread() { //主动和客户端断开连接 if (socketList.size() != 0) { for (int i = 0; i < socketList.size(); i++) { socketList[i]->disconnectFromHost(); socketList[i]->close(); socketList[i] = NULL; delete socketList[i]; } } delete tcpServer; }
遇到的问题:
1、心跳报文中为发送4个字节长度报文:两个quint16类型的数据均占2个字节,甲方并要求使用小端模式
解决办法:使用QByteArray字节数组进行写入
2、数据报文中,需使用ushort类型2个占4个字节和48个字节字符串共52个字节
解决办法:前面ushort类型使用QDataStraem类型写到QByteArray中。后面字符串使用char *类型append添加到QByteArray中,这样共52个字节
客户端:读取数据
。cpp文件:
#include "Client.h" #pragma execution_character_set("utf-8") Client::Client(QWidget *parent) : QWidget(parent) { ui.setupUi(this); } //连接按钮|获取服务器地址和端口号 void Client::on_Connect_Button_clicked() { socket = new QTcpSocket(this); socket->connectToHost(ui.leip->text(),ui.leport->text().toInt()); connect(socket,SIGNAL(readyRead()),this,SLOT(receiveMessage())); } //发送按钮|获取文本框内容 void Client::on_Send_Button_clicked() { QString str = ui.tesend->toPlainText(); socket->write(str.toUtf8());//中文不乱码 } //接收信息显示 void Client::receiveMessage() { qDebug() << socket->bytesAvailable() << "========";//4\52 if (socket->bytesAvailable() <= 0) { return; } QByteArray buffer; buffer = socket->readAll(); //m_buffer.append(buffer); qDebug() << buffer.size(); //4或者52 if (buffer.size() == 4) { QString s = buffer.mid(4); if (s == "ABC" ) { //QString str = QString(buffer); ui.tereceive->append(s); } qDebug() << buffer.mid(0,2).toInt(); //0 qDebug() << buffer.mid(2, 2).toInt();//0 QDataStream packet(buffer); packet.setByteOrder(QDataStream::LittleEndian); quint16 len, ID; packet >> len >>ID; qDebug() << len <<ID;//4 1 ui.tereceive->append(QString::number(len)); //4 ui.tereceive->append(QString::number(ID)); //1 } if (buffer.size() == 52) { /*quint16 length = (quint16)buffer.mid(0,2).toInt(); quint16 packID = (quint16)buffer.mid(2, 2).toInt();*/ //qDebug() << "报文长度:" << length << " 报文ID:" << packID;//报文长度: 4 报文ID: 0 //--------------------------------------------------------------------------------------- QDataStream OCRpacket(buffer); OCRpacket.setByteOrder(QDataStream::LittleEndian); quint16 ocr_length, ocr_ID; OCRpacket >> ocr_length >> ocr_ID; qDebug() << "报文长度2:" << ocr_length << " 报文ID2:" << ocr_ID; //-------------------------------------------------------------------------------------------- QString OCRstr = QString::fromStdString(buffer.mid(4, 24).toStdString()); qDebug() << OCRstr; QString s_dateTome = QString::fromStdString(buffer.mid(28, 52).toStdString()); qDebug() << s_dateTome; ui.tereceive->append(OCRstr); ui.tereceive->append(s_dateTome); }
原文:https://www.cnblogs.com/ivint/p/14368274.html