qt-基于QTcpSocket的tcp客户端

QAbstractSocket类发出的报错信号error(QAbstractSocket::SocketError)在个人使用过程中发现一个细节,经查找资料后做了信号连接异步处理:
当服务端关闭的时候,错误码为RemoteHostClosedError,可状态码却还是ConnectedState,这时候需要确保该报错流程生命周期完结,以使得可以得到应得的其它状态码;
connect(this, &TcpClient::signal_disconnected, this, &TcpClient::slot_disconnected, Qt::QueuedConnection);
源码中,我使用Qt::QueuedConnection来关联连接,以保证上文提到的生命周期完结;

具体源码如下:

TcpClient.h

#ifndef TcpClient_H
#define TcpClient_H

#include <QTcpSocket>
#include <QTimer>

/** 配套ColorTestServer(服务端)使用的客户端(含心跳和重连),输出状态及接收到的消息 */
class TcpClient : public QObject
{
    Q_OBJECT

public:
    TcpClient(QObject *parent = nullptr);
    ~TcpClient();

    /** 创建客户端:server_ip-服务端ip地址,server_tcp_port-服务端tcp端口号 */
    void CreateClient(const QString &server_ip, const unsigned int &server_tcp_port);
    /** 释放客户端 */
    void ReleaseClient();
    /** 发送消息:client_msg-客户端消息 */
    bool ClientSend(const char *client_msg);

private:
    /** 服务端ip地址 */
    QString m_server_ip;
    /** 服务端tcp端口号 */
    unsigned int m_server_tcp_port;
    /** tcp客户端 */
    QTcpSocket *m_TcpSocket;
    /** 定时器:心跳 */
    QTimer *m_Timer;
    /** 接收缓存 */
    std::string m_RecvBuf;

private slots:
    /** 连接 */
    void slot_TcpSocket_connected();
    /** 错误 */
    void slot_TcpSocket_error(QAbstractSocket::SocketError);
    /** 断开 */
    void slot_disconnected();
    /** 定时器:心跳 */
    void slot_Timer_timeout();
    /** 待读取 */
    void slot_TcpSocket_readyRead();

signals:
    /** 断开 */
    void signal_disconnected();
    /** 输出状态及接收到的消息:state-状态(0断开、1建立连接、2收到消息),server_msg-服务端消息(状态为2时才有数据),server_msg_len-服务端消息长度 */
    void signal_StateAndMsgOut(const unsigned int &state, const char *server_msg, const unsigned int &server_msg_len);
};

#endif
TcpClient.cpp
#include "TcpClient.h"

TcpClient::TcpClient(QObject *parent) : QObject(parent)
{
    m_server_ip = "";
    m_server_tcp_port = 0;
    m_TcpSocket = nullptr;
    m_Timer = nullptr;
    m_RecvBuf = "";
    connect(this, &TcpClient::signal_disconnected, this, &TcpClient::slot_disconnected, Qt::QueuedConnection);
}

TcpClient::~TcpClient()
{
    ReleaseClient();
}

void TcpClient::CreateClient(const QString &server_ip, const unsigned int &server_tcp_port)
{
    if(m_TcpSocket != nullptr)
    {
        return ;
    }

    m_server_ip = server_ip;
    m_server_tcp_port = server_tcp_port;
    m_TcpSocket = new QTcpSocket();
    connect(m_TcpSocket, &QTcpSocket::connected, this, &TcpClient::slot_TcpSocket_connected);
    connect(m_TcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slot_TcpSocket_error(QAbstractSocket::SocketError)));
    connect(m_TcpSocket, &QTcpSocket::readyRead, this, &TcpClient::slot_TcpSocket_readyRead);
    m_TcpSocket->connectToHost(m_server_ip, m_server_tcp_port);
    m_Timer = new QTimer();
    connect(m_Timer, &QTimer::timeout, this, &TcpClient::slot_Timer_timeout);
    m_Timer->start(60000);
}

void TcpClient::ReleaseClient()
{
    if(m_Timer != nullptr)
    {
        m_Timer->stop();
        delete m_Timer;
        m_Timer = nullptr;
    }
    if(m_TcpSocket != nullptr)
    {
        delete m_TcpSocket;
        m_TcpSocket = nullptr;
    }
}

void TcpClient::slot_TcpSocket_connected()
{
    emit signal_StateAndMsgOut(1, nullptr, 0);
}

void TcpClient::slot_TcpSocket_error(QAbstractSocket::SocketError)
{
    emit signal_disconnected();//这个信号关联的时候必须用Qt::QueuedConnection异步处理,因为当服务端关闭的时候,错误码为RemoteHostClosedError,可状态码却还是ConnectedState,即先需要该报错流程生命周期完结
}

void TcpClient::slot_disconnected()
{
    emit signal_StateAndMsgOut(0, nullptr, 0);
    m_TcpSocket->abort();
    m_TcpSocket->connectToHost(m_server_ip, m_server_tcp_port);
}

void TcpClient::slot_Timer_timeout()
{
    if(m_TcpSocket->state() == QTcpSocket::ConnectedState)
    {
        std::string ssSend = (char)0x02 + std::string("{\"type\":4}") + (char)0x03;
        m_TcpSocket->write(ssSend.c_str());
    }
}

void TcpClient::slot_TcpSocket_readyRead()
{
    m_RecvBuf += m_TcpSocket->readAll();
    while (true)
    {
        std::string::size_type ssst_pos = m_RecvBuf.find_first_of((char)0x03);
        if(ssst_pos != std::string::npos)
        {
            std::string ssCurRecv = m_RecvBuf.substr(1, ssst_pos-1);
            emit signal_StateAndMsgOut(2, ssCurRecv.c_str(), (unsigned int)ssCurRecv.length());
            m_RecvBuf = m_RecvBuf.substr(ssst_pos+1, m_RecvBuf.length()-ssst_pos);
        }
        else
        {
            if(m_RecvBuf.length() >= 1024*1024)
            {
                m_RecvBuf = "";
            }
            break;
        }
    }
}

bool TcpClient::ClientSend(const char *client_msg)
{
    if(m_TcpSocket == nullptr)
    {
        return false;
    }

    if(m_TcpSocket->state() != QTcpSocket::ConnectedState)
    {
        return false;
    }

    std::string ssSend = (char)0x02 + std::string(client_msg) + (char)0x03;
    if(m_TcpSocket->write(ssSend.c_str()) == -1)
    {
        return false;
    }

    return true;
}
源码是用于项目的,故里面有消息拆分的部分
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值