QTcpSocket 服务端和客户端

前提:

复制代码
pro文件中添加 QT += network

服务端主要采用信号槽机制,代码如如下

cpp 复制代码
核心代码头文件

#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QObject>

#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QMutex>
#include <QByteArray>
#include <QQueue>


class OneSession{
    public:
    QTcpSocket* m_clientSocket;
    QByteArray m_buffer;
    QQueue<QByteArray> m_msgQueue;
    QString m_carName;
    int m_id;
    QString m_longitude;
    QString m_latitude;
    QString m_altitude;
    OneSession(){
        m_id = 0;
    }

    ~OneSession(){
    }
};

class CenterTcpSvr : public QTcpServer
{
    Q_OBJECT
public:
    CenterTcpSvr(QObject *parent = nullptr)
        : QTcpServer(parent)
    {
    }

    QList<OneSession> m_sessions;
    QMutex m_mutexForSessions;
protected:
    void incomingConnection(qintptr socketDescriptor) override;
    void readyReadFun();
    void clientDisconnectedFun();
    void printAllSessions();

    int parseHead(OneSession& _one);
    int parseCmdAndDeal(OneSession& _one);
};

#endif // TCPSERVER_H


核心代码源文件

#include "centertcpsvr.h"




void CenterTcpSvr::incomingConnection(qintptr socketDescriptor)
{
    QTcpSocket *socket = new QTcpSocket(this);
    if (!socket->setSocketDescriptor(socketDescriptor)) {
        qDebug() << "Error setting socket descriptor";
        socket->deleteLater();
        return;
    }

    { //该括号配合锁使用,请不要随意删除,避免造成锁范围扩大
        QMutexLocker locker(&m_mutexForSessions); // 使用QMutexLocker自动管理互斥锁
        OneSession one;
        one.m_clientSocket = socket;
        m_sessions.append(one);

        printAllSessions();
    }
   // QTcpSocket::connected
    connect(socket, &QTcpSocket::readyRead, this, &CenterTcpSvr::readyReadFun);
    connect(socket, &QTcpSocket::disconnected, this, &CenterTcpSvr::clientDisconnectedFun);
}

struct ProtocolHead{
    char flag[4];
    char version[4];
    char id[4]; //自己的ID
    char cmd[4]; //1,注册;2,发给教辅;其他为车标号目标
    char bodySize[4];
};

struct UserProtocolForward{
    char dst[4];
};


int CenterTcpSvr::parseHead(OneSession& _one)
{
    int ret = -1;

    while (_one.m_buffer.size() > sizeof(ProtocolHead)) {

        char* tmpBuf = _one.m_buffer.data();

        if (_one.m_id == 0) {
            unsigned  id =     ((unsigned int)tmpBuf[8]  << 24 & 0xFF000000)
                                +((unsigned int)tmpBuf[9]  << 16 & 0x00FF0000)
                                +((unsigned int)tmpBuf[10] <<  8 & 0x0000FF00)
                                +((unsigned int)tmpBuf[11]       & 0x000000FF);
            _one.m_id = id;
        }

        unsigned  bodySize = ((unsigned int)tmpBuf[16]  << 24 & 0xFF000000)
                            +((unsigned int)tmpBuf[17]  << 16 & 0x00FF0000)
                            +((unsigned int)tmpBuf[18] <<  8 & 0x0000FF00)
                            +((unsigned int)tmpBuf[19]       & 0x000000FF);
        qDebug() << "bodySize is:" << bodySize;

        int outSize = sizeof(ProtocolHead) + bodySize;
        if (_one.m_buffer.size() >= outSize) {
            _one.m_msgQueue.enqueue(_one.m_buffer.left(outSize));
            _one.m_buffer.remove(0, outSize);
            ret = 0;
        } else {
            break;
        }
    }

    return  ret;
}


int CenterTcpSvr::parseCmdAndDeal(OneSession& _one)
{
    int ret = 0;

    while (_one.m_msgQueue.size() > 0) {
        QByteArray bAarray = _one.m_msgQueue.dequeue();
        qDebug() << "dequeue content is:" << bAarray;

        char* tmpBuf = bAarray.data();


        unsigned  cmd =     ((unsigned int)tmpBuf[12]  << 24 & 0xFF000000)
                            +((unsigned int)tmpBuf[13]  << 16 & 0x00FF0000)
                            +((unsigned int)tmpBuf[14] <<  8 & 0x0000FF00)
                            +((unsigned int)tmpBuf[15]       & 0x000000FF);

        if (cmd == 1)
        { //注册

        } else if (cmd == 2) { //发送给教辅

        } else {
            bool isIn = false;
            QList<OneSession>::iterator it;
            for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {
                qDebug() << "cmd is : " << cmd  << "_one.m_id: " << _one.m_id ;
                if (cmd == it->m_id) { // 进到这里表示发送目标
                    it->m_clientSocket->write(bAarray.data(), bAarray.size());
                    it->m_clientSocket->waitForBytesWritten(1000);
                }
            }
            if (isIn) {

                ProtocolHead sendDataStruct;
                QByteArray sd;
                char body[] = {"{\"code\":0, \"msg\":OK}"};
                int bsize = sizeof(body);
                memcpy(sendDataStruct.flag, "#GD#", 4);
                memcpy(sendDataStruct.version, "0001", 4);

                sendDataStruct.bodySize[0] = 0 >> 24 & 0x000000FF;
                sendDataStruct.bodySize[1] = 0 >> 16 & 0x000000FF;
                sendDataStruct.bodySize[2] = 0 >> 8 & 0x000000FF;
                sendDataStruct.bodySize[3] = 0 & 0x000000FF;

                char* csend = new char[sizeof (ProtocolHead)+bsize];
                memset(csend, 0, sizeof(ProtocolHead) + bsize);
                memcpy(csend, (char*)(&sendDataStruct), sizeof(ProtocolHead));

                //
                _one.m_clientSocket->write(csend, sizeof(ProtocolHead) + bsize);
                _one.m_clientSocket->waitForBytesWritten(1000);

                delete []csend;
            }
        }
    }

    return ret;
}

void CenterTcpSvr::readyReadFun() {
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
    //qDebug() << "readyReadData :" << clientSocket;
    QByteArray data = clientSocket->readAll();
    qDebug() << "data :" << data;


    QList<OneSession>::iterator it;
    for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {
        if(clientSocket == it->m_clientSocket) {
            it->m_buffer.append(data);
            break;
        }
    }

    if (-1 == parseHead(*it)) { //解析头并分包放入队列
        return;
    }

    parseCmdAndDeal(*it);

}


void CenterTcpSvr::printAllSessions() { //此处没有上锁,请在上层使用处控制
    qDebug() << "clientDisconnectedFun begin. <-----------";
    QList<OneSession>::iterator it;
    for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {
        qDebug() << "所有会话:::" << it->m_clientSocket;
    }
    qDebug() << "clientDisconnectedFun   end. ----------->";
}

void CenterTcpSvr::clientDisconnectedFun() {
    QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
    qDebug() << "clientDisconnected :" << clientSocket;

        if (clientSocket) {
            // 移除断开连接的客户端socket
            QMutexLocker locker(&m_mutexForSessions); // 使用QMutexLocker自动管理互斥锁
            QList<OneSession>::iterator it;
            for(it = m_sessions.begin(); it != m_sessions.end(); ++it) {
                //qDebug() << "所有会话:" << clientSocket;
                if (it->m_clientSocket == clientSocket)
                {
                    clientSocket->deleteLater();
                    m_sessions.erase(it);
                    qDebug() << "删除会话:" << clientSocket;
                    break;
                }
            }
            printAllSessions();
        }
}


调用上述代码头文件

#ifndef TEST_H
#define TEST_H

#include <QObject>

#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include <QThread>
#include <QString>
#include "centertcpsvr.h"

class ThreadForCenterTcpSvr : public QThread
{
    Q_OBJECT
public:
    ThreadForCenterTcpSvr();

    void run(); //线程任务函数

signals:

    void startsignal();  //启动⼦线程的信号
};

#endif // TEST_H

调用上述代码源文件

#include "threadforcentertcpsvr.h"
#include <QCoreApplication>
#include "centertcpsvr.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
#include <QThread>
#include <QString>

ThreadForCenterTcpSvr::ThreadForCenterTcpSvr()
{

}


void ThreadForCenterTcpSvr::run()
{
     while (true)
     {
         CenterTcpSvr* server = new CenterTcpSvr();
         if (!server->listen(QHostAddress::Any, 8080)) {
             qDebug() << "Server failed to start";
             return;
         }
         qDebug() << "Server start";

         QEventLoop loop;
         loop.exec(); // 进入事件循环,直到请求完成
         //QThread::exec(); //或者用这个也可以
     }
}


在 main 函数中启动服务


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    ThreadForCenterTcpSvr* p = new ThreadForCenterTcpSvr();
    p->start();
    return a.exec();
}

客户端有两种实现方式

第一种方式如下,非信号槽方式实现

cpp 复制代码
#include <QCoreApplication>

#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>


struct ProtocolHead{
    char flag[4];
    char version[4];
    char id[4]; //自己的ID
    char cmd[4]; //0,注册;1,发给教辅;其他为车标号目标
    char bodySize[4];
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);



    QTcpSocket socket;


    while (true) {
        // 连接到服务器
        socket.connectToHost("localhost", 8080);
        // 等待连接
        if(socket.waitForConnected(100000)) {
            qDebug() << "Connected!";

            int c = 0;
            while (true) {
                // 发送数据
                ProtocolHead sendDataStruct;
                QByteArray sd;
                char body[] = "Hello server!";
                int bsize = sizeof(body);
                memcpy(sendDataStruct.flag, "#GD#", 4);
                memcpy(sendDataStruct.version, "0001", 4);

                int id = 48102; //自己的标识
                sendDataStruct.id[0] = id >> 24 & 0x000000FF;
                sendDataStruct.id[1] = id >> 16 & 0x000000FF;
                sendDataStruct.id[2] = id >> 8 & 0x000000FF;
                sendDataStruct.id[3] = id & 0x000000FF;

                int cmd = 48102; //对方的标识
                sendDataStruct.cmd[0] = cmd >> 24 & 0x000000FF;
                sendDataStruct.cmd[1] = cmd >> 16 & 0x000000FF;
                sendDataStruct.cmd[2] = cmd >> 8 & 0x000000FF;
                sendDataStruct.cmd[3] = cmd & 0x000000FF;

                sendDataStruct.bodySize[0] = bsize >> 24 & 0x000000FF;
                sendDataStruct.bodySize[1] = bsize >> 16 & 0x000000FF;
                sendDataStruct.bodySize[2] = bsize >> 8 & 0x000000FF;
                sendDataStruct.bodySize[3] = bsize & 0x000000FF;

                char* csend = new char[sizeof (ProtocolHead)+bsize];
                memset(csend, 0, sizeof(ProtocolHead) + bsize);

                int pos = 0;
                memcpy(csend+pos, (char*)(&sendDataStruct), sizeof(ProtocolHead));
                pos += sizeof(ProtocolHead);
                memcpy(csend+pos, body, bsize);

                socket.write(csend, sizeof (ProtocolHead)+bsize);

                // 等待服务器响应
                if(socket.waitForBytesWritten(1000))
                {
                    if (c%250000==0) {
                        qDebug() << "Message sent!";
                    }

                    // 等待服务器的响应
                    while(!socket.waitForReadyRead(1000)) {
                        //QByteArray qb = socket.readAll();

                        if (socket.error() == QAbstractSocket::SocketTimeoutError) {
                            qDebug() << "超时错误:没有在指定时间内接收到数据";
                        }
                        if (socket.error() == QAbstractSocket::RemoteHostClosedError) {
                            qDebug() << "远程主机关闭。";


                            while (true) {
                                //socket.abort();
                                socket.connectToHost("localhost", 8080);
                                // 等待连接
                                if(socket.waitForConnected(1000)) {
                                    qDebug() << "Connected!";
                                    break;
                                }
                            }

                        } else {
                            qDebug() << "其他错误:" << socket.errorString();
                        }

                    }
                    QByteArray qb = socket.readAll();
                    qDebug() << c << ", ======Response received:" << qb;

                } else {
                    qDebug() << "Failed to send message";
                }
                //break;
                c++;
                if (c/250000==10) {
                    c=0;
                }
            }
            // 关闭连接
            socket.close();
        } else {
            qDebug() << "Failed to connect";
        }
        QThread::sleep(1);
        //break;
    }

    return a.exec();
}

第二种方式,信号槽方式

cpp 复制代码
文件1

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QObject>
#include <QTcpSocket>
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>

class TcpClient : public QObject
{
    Q_OBJECT
public:
    explicit TcpClient(QObject *parent = nullptr);

    ~TcpClient();

    void connectToServer(const QString &host, quint16 port);

private slots:
    void onConnected();
    void onDisconnected();
    void onReadyRead();
    void onBytesWritten(qint64 bytes);

private:
    QTcpSocket *socket;
signals:

};

#endif // TCPCLIENT_H

文件2

#include "tcpclient.h"

TcpClient::TcpClient(QObject *parent) : QObject(parent), socket(new QTcpSocket(this))
{

    connect(socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
    connect(socket, &QTcpSocket::disconnected, this, &TcpClient::onDisconnected);
    connect(socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
    connect(socket, &QTcpSocket::bytesWritten, this, &TcpClient::onBytesWritten);
}

TcpClient::~TcpClient()
{
    delete socket; // Qt 自动管理内存,这里只是示例
}

void TcpClient::connectToServer(const QString &host, quint16 port)
{
    socket->connectToHost(host, port);
}

void TcpClient::onConnected()
{
    qDebug() << "Connected to server";
    socket->write("Hello, Server!");
}

void TcpClient::onDisconnected()
{
    qDebug() << "Disconnected from server";
}

void TcpClient::onReadyRead()
{
    while (socket->canReadLine())
    {
        QString line = socket->readLine().trimmed();
        qDebug() << "Received from server:" << line;
    }
}

void TcpClient::onBytesWritten(qint64 bytes)
{
    qDebug() << "Sent" << bytes << "bytes to server";
}

文件3

#include <QCoreApplication>


#include <QTcpSocket>
#include <QObject>
#include <QTcpSocket>
#include <QThread>
#include <QByteArray>
#include <QAbstractSocket>
#include "tcpclient.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    TcpClient client;
    client.connectToServer("127.0.0.1", 8080); // 替换为实际的服务器 IP 和端口


    return a.exec();
}
相关推荐
SomeB1oody7 分钟前
【Rust自学】5.1. 定义并实例化struct
开发语言·后端·rust
云空29 分钟前
《解锁 Python 数据挖掘的奥秘》
开发语言·python·数据挖掘
青莳吖39 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall1 小时前
期末考学C
java·开发语言
重生之绝世牛码1 小时前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行1 小时前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
Algorithm15761 小时前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
shinelord明1 小时前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程
Monly211 小时前
Java(若依):修改Tomcat的版本
java·开发语言·tomcat
boligongzhu1 小时前
DALSA工业相机SDK二次开发(图像采集及保存)C#版
开发语言·c#·dalsa