以自定數據包格式進行通信
數據包格式如下:
服務端代碼如下:
。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); }