Qt TCP Client

本文详细介绍了一个基于QTcpSocket的客户端实现,包括连接、断开、错误处理、数据读写及解析流程。通过示例代码展示了如何在多线程环境下进行网络通信,并介绍了数据包的解析与业务处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

头文件tcpclient.h

#ifndef TCPCLIENT_H
#define TCPCLIENT_H

#include <QObject>
#include <QTcpSocket>
#include <QDebug>
#include "enthernetdataparsing.h"
#include "tf500enthernetdataparsing.h"

class TCPClient : public QObject
{
    Q_OBJECT
public:
    explicit TCPClient(QObject *parent = 0);
    
signals:
    void reConnect();
    
public slots:
    void slot_connectServer(QString serverIP, quint16 port);

    void slot_reConnectServer();

private slots:
    void processError(QAbstractSocket::SocketError socketError);

    void readData();

    qint64 writeData(const char * data, qint64 len);

    void hostFound();

    void connected();

private:
    QTcpSocket *tcpSocket;

    QString serverIP;

    quint16 port;

    char *recvBuf;

    EnthernetDataParsing *enthernetDataParsing;

private:
    void reConnectServer();

    void allocMemory();

    void disconnectFromServer();

public:
    void connectServer(QString serverIP, quint16 port);
    
};

#endif // TCPCLIENT_H

.cpp

#include <QThread>
#include <QtEndian>
#include <syslog.h>
#include "tcpclient.h"
#include "printfhex.h"

TCPClient::TCPClient(QObject *parent) :
    QObject(parent)
{
    tcpSocket = new QTcpSocket(this);
    tcpSocket->setReadBufferSize(32 * 1024);

    enthernetDataParsing = new TF500EnthernetDataParsing();

    recvBuf = NULL;
    allocMemory();

    connect(enthernetDataParsing, SIGNAL(writeData(const char*,qint64)),
            this, SLOT(writeData(const char*,qint64)));

    connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(processError(QAbstractSocket::SocketError)));

    connect(tcpSocket, SIGNAL(hostFound()), this, SLOT(hostFound()));

    connect(tcpSocket, SIGNAL(connected()), this, SLOT(connected()));

    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readData()));

    connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(slot_reConnectServer()));

    connect(this, SIGNAL(reConnect()), this, SLOT(slot_reConnectServer()));
}

void TCPClient::allocMemory()
{
    if(!recvBuf){
        recvBuf = new char[MAX_RECVBUF_LEN];
        memset(recvBuf, 0, MAX_RECVBUF_LEN);
    }

    if(!recvBuf)
    {
        qDebug("Alloc Memory error in File:%s Line:%d.", __FILE__, __LINE__);
        syslog(LOG_ERR | LOG_USER, "Alloc Memory error in File:%s Line:%d.", __FILE__, __LINE__);
    }
}

void TCPClient::disconnectFromServer()
{
    tcpSocket->disconnectFromHost();
    bool ret = tcpSocket->waitForDisconnected(5000);
    qDebug() << "disconnectFromHost: " << ret;
}

void TCPClient::slot_connectServer(QString serverIP, quint16 port)
{
    connectServer(serverIP, port);
}

void TCPClient::slot_reConnectServer()
{
    reConnectServer();
}

void TCPClient::processError(QAbstractSocket::SocketError socketError)
{
    switch(socketError){
    case QAbstractSocket::ConnectionRefusedError:
        qDebug() <<  tr("The connection was refused by the peer (or timed out).");
        emit reConnect();
        break;
    case QAbstractSocket::RemoteHostClosedError:
        qDebug() <<  tr("Remote host closed the connection.");
        break;
    case QAbstractSocket::HostNotFoundError:
        qDebug() <<  tr("The host was not found. Please check the host name and port settings.");
        break;
//    case QAbstractSocket::SocketAccessError:
//        break;
//    case QAbstractSocket::SocketResourceError:
//        break;
//    case QAbstractSocket::SocketTimeoutError:
//        break;
//    case QAbstractSocket::DatagramTooLargeError:
//        break;
    case QAbstractSocket::NetworkError: //e.g., the network cable was accidentally plugged out
        qDebug() <<  tr("An error occurred with the network.");
        emit reConnect();
        break;
//    case QAbstractSocket::UnsupportedSocketOperationError:
//        break;
    default:
        qDebug() << tr("The following error occurred: %1.").arg(tcpSocket->errorString());
        break;
    }
}

void TCPClient::readData()
{
    int recvLen = 0, onceRecvLen = 0;

    while(1){
        if(recvLen < FRAME_HEAD_LEN){
            onceRecvLen = tcpSocket->read(recvBuf + recvLen, FRAME_HEAD_LEN - recvLen);
            if(onceRecvLen == -1){
                tcpSocket->close();
                break;
            }
            recvLen += onceRecvLen;
        }
        else{
            int frameLen = 0;
            memcpy(&frameLen, recvBuf + START_FRAME_LEN, 4);
            frameLen = qToBigEndian(frameLen);

            if(recvLen < frameLen){
                onceRecvLen = tcpSocket->read(recvBuf + recvLen, frameLen - recvLen);
                if(onceRecvLen == -1){
                    tcpSocket->close();
                    break;
                }
                recvLen += onceRecvLen;
            }
            else if(recvLen == frameLen){
                PrintfHex printfHex;
                printf("Receive data:");
                printfHex.printfHex(recvBuf, FRAME_HEAD_LEN, true);
                //process
                enthernetDataParsing->parseData(recvBuf, recvLen);
                break;
            }
            else{
                tcpSocket->readAll();
                break;
            }
        }
    }

    qDebug() << "readBytes:" << recvLen;
    qDebug() << "readData in thread:" << QThread::currentThread();
}

qint64 TCPClient::writeData(const char *data, qint64 len)
{
    qDebug() << "write data in TCPClient.";

    qint64 onceWriteLen = 0, sendLen = 0;

//    onceWriteLen = tcpSocket->write(data + onceWriteLen, len - onceWriteLen);
}

void TCPClient::hostFound()
{
    qDebug() << "Host found success.";
}

void TCPClient::connected()
{
    qDebug() << QString("Connect to ip:%1 port:%2 success.").arg(serverIP).arg(port);
}

void TCPClient::connectServer(QString serverIP, quint16 port)
{
    this->serverIP = serverIP;
    this->port = port;
    tcpSocket->connectToHost(serverIP, port);
    tcpSocket->waitForConnected(5000);
}

void TCPClient::reConnectServer()
{
    sleep(2);
    tcpSocket->connectToHost(serverIP, port);
    tcpSocket->waitForConnected(5000);
}

Printfhex是一个打印char数组的类,以16进制打印。

EnthernetDataParsing是一个纯虚类,解析收到的数据,源码如下:

#ifndef ENTHERNETDATAPARSING_H
#define ENTHERNETDATAPARSING_H

#include <QObject>

class EnthernetDataParsing : public QObject
{
    Q_OBJECT
public:
    explicit EnthernetDataParsing(QObject *parent = 0);
    
signals:
    void writeData(const char * data, qint64 len);
    
public slots:

public:
    virtual int parseData(char *data, qint64 len) = 0;
    
};

#endif // ENTHERNETDATAPARSING_H

TF500EnthernetDataParsing继承自EnthernetDataParsing,是按照具体协议解析数据的类。

解析出命令后调用完成对应业务的类,执行完成后发送执行结果。

一般会把TCPClient放到线程中执行:

#include <QtCore/QCoreApplication>
#include <QThread>
#include "tcpclient.h"
#include "tcpclientop.h"

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

    TCPClient tcpClient;
    TCPClientOP tcpClientOP;

    QThread thread;
    tcpClient.moveToThread(&thread);
    QObject::connect(&tcpClientOP, SIGNAL(connectServer(QString,quint16)),
                     &tcpClient, SLOT(slot_connectServer(QString,quint16)));
    thread.start();

    tcpClientOP.startConnect("10.0.10.55", 11866);

    qDebug() << "main thread:" << QThread::currentThread();

    return a.exec();
}

TCPClientOP

#ifndef TCPCLIENTOP_H
#define TCPCLIENTOP_H

#include <QObject>

class TCPClientOP : public QObject
{
    Q_OBJECT
public:
    explicit TCPClientOP(QObject *parent = 0):QObject(parent){};
    
signals:
    void connectServer(QString serverIP, quint16 port);
    
public slots:

public:
    void startConnect(QString serverIP, quint16 port)
    {
        emit connectServer(serverIP, port);
    }
    
};

#endif // TCPCLIENTOP_H

 

### 使用 Qt 创建 TCP 服务器和客户端 #### 创建 TCP 服务器 为了创建一个简单的 TCP 服务器,在 Qt 中可以利用 `QTcpServer` 类来监听传入连接请求。下面是一个基本的例子: ```cpp #include <QCoreApplication> #include <QTcpServer> #include <QTcpSocket> class TcpServer : public QTcpServer { Q_OBJECT public: explicit TcpServer(QObject *parent = nullptr) : QTcpServer(parent) {} protected: void incomingConnection(qintptr socketDescriptor) override { qDebug() << "New connection:" << socketDescriptor; QTcpSocket* sock = new QTcpSocket(this); if (!sock->setSocketDescriptor(socketDescriptor)) { delete sock; return; } connect(sock, &QTcpSocket::readyRead, [&]() { handleReadyRead(sock); }); connect(sock, &QTcpSocket::disconnected, sock, &QTcpSocket::deleteLater); // Add more initialization here as needed. } private slots: void handleReadyRead(QTcpSocket* socket) { QByteArray data = socket->readAll(); qDebug() << "Received from client:" << data; // Echo back the received message to the sender socket->write(data); socket->flush(); // Close after echoing; modify according to your needs socket->close(); } }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); TcpServer server; if(!server.listen(QHostAddress::Any, 1234)){ qCritical() << "Failed to start server!"; exit(1); } else { qDebug() << "Listening..."; } return app.exec(); } ``` 这段代码定义了一个继承自 `QTcpServer` 的类,并重写了其虚函数 `incomingConnection()` 来处理新的连接事件[^1]。 #### 创建 TCP 客户端 对于客户端部分,则可以通过 `QTcpSocket` 实现,这是一个用于建立到服务器的TCP/IP 连接并发送/接收数据的对象实例。这里给出一段基础示例代码展示如何构建一个简单的客户端程序: ```cpp #include <QCoreApplication> #include <QTcpSocket> #include <QDebug> void setupClient(const QString& hostName, quint16 port){ QTcpSocket tcpSocket; QObject::connect(&tcpSocket, &QTcpSocket::connected, [](){ qDebug() << "Connected."; }); QObject::connect(&tcpSocket, &QTcpSocket::readyRead, [&tcpSocket]{ while (tcpSocket.canReadLine()) { qDebug().noquote() << "Message from server:" << tcpSocket.readLine(); } }); tcpSocket.connectToHost(hostName, port); } int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); const QString hostname = "localhost"; // Replace with actual IP address or domain name const quint16 portNumber = 1234; // Use same port number used by server setupClient(hostname, portNumber); return a.exec(); } ``` 此段代码展示了怎样设置一个能够向指定地址发起连接尝试的客户端应用,并实现了当成功连接以及有可读取的数据到来时触发相应槽函数的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值