QT中TCP通信及遇到的問題


以自定數據包格式進行通信

數據包格式如下:

服務端代碼如下:

。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);
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM