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();
}
相关推荐
杨荧3 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的宠物咖啡馆平台
java·开发语言·jvm·vue.js·spring boot·spring cloud·开源
monkey_meng15 分钟前
【Rust中的项目管理】
开发语言·rust·源代码管理
喜欢打篮球的普通人17 分钟前
rust高级特征
开发语言·后端·rust
ModelBulider35 分钟前
十三、注解配置SpringMVC
java·开发语言·数据库·sql·mysql
V搜xhliang024644 分钟前
基于深度学习的地物类型的提取
开发语言·人工智能·python·深度学习·神经网络·学习·conda
DK七七1 小时前
多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码
开发语言·前端·微信小程序·小程序·php
苹果酱05671 小时前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
代码小鑫1 小时前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
训山1 小时前
4000字浅谈Java网络编程
java·开发语言·网络
API快乐传递者1 小时前
除了网页标题,还能用爬虫抓取哪些信息?
开发语言·爬虫·python