0
点赞
收藏
分享

微信扫一扫

Qt网络编程1-TCPClient入门Demo

kiliwalk 2022-04-13 阅读 51

Qt网络编程🔺

文章目录

1、TCP Client🔗

使用QTcpSocket实现TCP Client,十分详细的入门Demo

1.1 示例代码结构👀

在这里插入图片描述

1.2 实现效果🙉

1.3 关键信号🎻

信号说明
connectedQTcpSocket连接成功后发出此信号
disconnectedQTcpSocket断开连接后发出此信号
stateChangedQTcpSocket状态发生改变后发出此信号(不必要)
readyRead有可读数据时发出此信号
errorOccurredQTcpSocket发生异常时发出此信号,qt5.15 后error()信号已经弃用

1.4 关键函数 💍

函数名说明
m_tcpClient->connectToHost(IP, 端口)TCP连接Server
m_tcpClient->abort()立即关闭套接字,丢弃写入缓冲区中的任何未决数据
m_tcpClient->close()关闭 QIODevice并调用disconnectFromHost
m_tcpClient->disconnectFromHost()如果有待写入的数据等待写入,等待所有数据写入完毕(不一定立即关闭)[不推荐使用]
m_tcpClient->state()判断QTcpSocket当前状态,UnconnectedState未连接、ConnectedState已连接
m_tcpClient->waitForDisconnected(1000)等待关闭连接,如果使用close或disconnectFromHost关闭连接则可选择使用这个函数
m_tcpClient->write(QByteArray)发送数据
m_tcpClient->readAll()读取所有数据

1.5 主要代码🌈

#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QHostAddress>
#include <QDebug>
#include <QByteArray>
#include <QHostInfo>

TCPClient::TCPClient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::TCPClient)
{
    ui->setupUi(this);
    this->setWindowTitle("TCP客户端Demo");

    init();
    connectSlots();
}

TCPClient::~TCPClient()
{
#ifdef QT_DEBUG
    qDebug() <<"~TCPClient()";
#endif
    delete ui;
}

/**
 * @brief  获取本机IPv4地址
 * @return
 */
QString getLocalIP()
{
    QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());

    for(auto address : info.addresses())
    {
        if(address.protocol() == QAbstractSocket::IPv4Protocol)
        {
            return address.toString();
        }
    }

    return "0.0.0.0";
}

void TCPClient::init()
{
    m_tcpClient = new QTcpSocket(this);
    ui->line_localAddress->setText(getLocalIP());
}

void TCPClient::connectSlots()
{
    connect(m_tcpClient, &QTcpSocket::connected, this, &TCPClient::on_connected);
    connect(m_tcpClient, &QTcpSocket::disconnected, this, &TCPClient::on_disconnected);
    connect(m_tcpClient, &QTcpSocket::stateChanged, this, &TCPClient::on_stateChanged);
    connect(m_tcpClient, &QTcpSocket::readyRead, this, &TCPClient::on_readyRead);

#if (QT_VERSION <= QT_VERSION_CHECK(5,15,0))  // qt5.15 后error已经弃用,这里改用errorOccurred
    connect(m_tcpClient, &QTcpSocket::error, this, &TCPClient::on_errorOccurred);
#else
    connect(m_tcpClient, &QTcpSocket::errorOccurred, this, &TCPClient::on_errorOccurred);
#endif
}

/**
 * @brief 调用 connectToHost() 并成功建立连接。
 */
void TCPClient::on_connected()
{
    ui->but_connect->setText("断开连接");
    ui->spin_localPort->setValue(m_tcpClient->localPort());
    qInfo() << QString("成功连接:%1 %2").arg(m_tcpClient->peerName()).arg(m_tcpClient->peerPort());
}

/**
 * @brief Socket已断开连接
 */
void TCPClient::on_disconnected()
{
    ui->but_connect->setText("连接");
    qInfo() <<QString("断开连接:%1 %2").arg(m_tcpClient->peerName()).arg(m_tcpClient->peerPort());
}

/**
 * @brief       QAbstractSocket 的状态发生变化
 * @param state QAbstractSocket::SocketState 不是注册的元类型,因此对于Qt::QueuedConnection信号槽连接类型,
 *              必须使用 Q_DECLARE_METATYPE() 和 qRegisterMetaType() 注册它。
 *              QAbstractSocket::UnconnectedState	0	套接字未连接。
 *              QAbstractSocket::HostLookupState	1	套接字正在查询主机。
 *              QAbstractSocket::ConnectingState	2	套接字开始建立连接。
 *              QAbstractSocket::ConnectedState	    3	新的连接已建立。
 *              QAbstractSocket::BoundState	        4	套接字已绑定到一个地址和端口。
 *              QAbstractSocket::ClosingState	    6	套接字即将关闭(数据可能仍在等待写入)。
 *              QAbstractSocket::ListeningState	    5	套接字仅限内部使用。
 */
void TCPClient::on_stateChanged(QAbstractSocket::SocketState state)
{
    qInfo() << "状态改变:"<< state;
}

/**
 * @brief        发生错误
 * @param error  错误异常信息,QAbstractSocket::SocketError 不是已注册的元类型,
 *               因此对于排队连接,您必须使用 Q_DECLARE_METATYPE() 和 qRegisterMetaType() 注册它。
 *               ConnectionRefusedError:连接被对等方拒绝(或超时)。
 *               RemoteHostClosedError:远程主机关闭了连接。请注意,客户端Socket将在发送远程关闭通知后关闭。
 *               HostNotFoundError:找不到主机地址。
 *               SocketAccessError:Socket操作失败,因为应用程序缺少所需的权限。
 *               SocketResourceError:本地系统资源不足(例如,Socket过多)。
 *               SocketTimeoutError:Socket操作超时。
 *               DatagramTooLargeError:数据报大于操作系统的限制。
 *               NetworkError:网络出现错误(例如,网现被拔出)。
 *               AddressInUseError:指定给 bind() 的地址已在使用中并设置为独占。
 *               SocketAddressNotAvailableError:指定给 bind() 的地址不属于主机。
 *               UnsupportedSocketOperationError:本地操作系统不支持请求的Socket操作(例如,缺乏 IPv6 支持)。
 *               ProxyAuthenticationRequiredError:Socket正在使用代理,并且代理需要身份验证。
 *               SslHandshakeFailedError:SSL/TLS 握手失败,因此连接被关闭(仅在 QSslSocket 中使用)
 *               UnfinishedSocketOperationError:上次尝试的操作尚未完成(仍在后台进行)。
 *               ProxyConnectionRefusedError:无法联系代理服务器,因为与该服务器的连接被拒绝
 *               ProxyConnectionClosedError:在与最终对等方的连接建立之前,与代理服务器的连接意外关闭
 *               ProxyConnectionTimeoutError:与代理服务器的连接超时或代理服务器在身份验证阶段停止响应。
 *               ProxyNotFoundError:未找到使用 setProxy()(或应用程序代理)设置的代理地址。
 *               ProxyProtocolError:与代理服务器的连接协商失败,因为无法理解来自代理服务器的响应。
 *               OperationError:当Socket处于不允许的状态时尝试进行操作。
 *               SslInternalError:正在使用的 SSL 库报告了一个内部错误。这可能是库安装错误或配置错误的结果。
 *               SslInvalidUserDataError:提供了无效数据(证书、密钥、密码等),其使用导致 SSL 库中出现错误。
 *               TemporaryError:发生临时错误(例如,操作会阻塞而Socket是非阻塞的)。
 *               UnknownSocketError:发生不明错误。
 */
void TCPClient::on_errorOccurred(QAbstractSocket::SocketError error)
{
    qWarning() << "出现异常:" <<error;
}

/**
 * @brief 接收数据
 */
void TCPClient::on_readyRead()
{
    QByteArray arr = m_tcpClient->readAll();

    if(arr.count() <= 0)
    {
        return;
    }
    ui->spin_recv->setValue(ui->spin_recv->value() + arr.count());
    if(ui->check_hexRecv->isChecked())
    {
        ui->text_recv->append(arr.toHex(' '));
    }
    else
    {
        ui->text_recv->append(arr);
    }
}

/**
 * @brief TCP连接
 */
void TCPClient::on_but_connect_clicked()
{
    if(!m_tcpClient) return;

    if(m_tcpClient->state() == QAbstractSocket::UnconnectedState)         // 判断连接状态
    {
        m_tcpClient->connectToHost(ui->line_peerAddress->text(), ui->spin_peerPort->value());
    }
    else
    {
#if 1   // 使用abort函数立即关闭套接字,丢弃写入缓冲区中的任何未决数据。
        m_tcpClient->abort();

#else   // 等待数据发送完成再关闭

#if 0
        m_tcpClient->disconnectFromHost();  // 不一定立即关闭,如果有待写入的数据等待写入,将进入 ClosingState状态 并等待所有数据写入完毕
#else
        m_tcpClient->close();               // 关闭 QIODevice并调用disconnectFromHost
#endif
        qDebug() <<"IO状态:" << m_tcpClient->isOpen();                 // 可通过这一行代码测试Close和disconnectFromHost的区别
        if (m_tcpClient->state() == QAbstractSocket::UnconnectedState) // 如果调用close后没有立即关闭则等待 1秒中
        {
            if(m_tcpClient->waitForDisconnected(1000))
            {
                qInfo() <<"关闭成功!";
            }
            else
            {
                qWarning() << "关闭失败:"<<m_tcpClient->error();
            }
        }
#endif
    }
}

/**
 * @brief 发送数据
 */
void TCPClient::on_but_send_clicked()
{
    if(!m_tcpClient) return;
    if(m_tcpClient->state() != QAbstractSocket::ConnectedState) return;  // 判断是否已连接

    QString str = ui->text_send->toPlainText();
#if 0
    QByteArray arr = str.toLocal8Bit();              // 根据编译器编码或者 QTextCodec::setCodecForLocale(codec);指定的编码方式将QString转换为QBytearray,一般为utf-8或者GBK,支持中文
#else
    QByteArray arr = str.toUtf8();                   // 指定以utf-8编码
#endif
    if(ui->check_hexSend->isChecked())               // 如果是16进制显示则转换
    {
        arr = QByteArray::fromHex(arr);
    }

    qint64 len = m_tcpClient->write(arr);
    if(len < 0)
    {
        qWarning() <<"发送失败!";
    }
    ui->spin_send->setValue(ui->spin_send->value() + len);
}

/**
 * @brief 清空接收区
 */
void TCPClient::on_but_clearRecv_clicked()
{
    ui->text_recv->clear();
    ui->spin_recv->setValue(0);
}

/**
 * @brief 清空发送区
 */
void TCPClient::on_but_clearSend_clicked()
{
    ui->text_send->clear();
    ui->spin_send->setValue(0);
}

/**
 * @brief           将发送区内容转换为十六进制显示
 * @param checked
 */
void TCPClient::on_check_hexSend_clicked(bool checked)
{
    QString value;
    if(checked)
    {
        value = ui->text_send->toPlainText().toUtf8().toHex(' ');
    }
    else
    {
        value = QByteArray::fromHex(ui->text_send->toPlainText().toUtf8());
    }
    ui->text_send->setText(value);
}

/**
 * @brief          将接收区内容转换为十六进制显示
 * @param checked
 */
void TCPClient::on_check_hexRecv_clicked(bool checked)
{
    QString value;
    if(checked)
    {
        value = ui->text_recv->toPlainText().toUtf8().toHex(' ');             // 01 02 03 1a 格式显示
    }
    else
    {
        value = QByteArray::fromHex(ui->text_recv->toPlainText().toUtf8());   // 123abc格式显示
    }
    ui->text_recv->setText(value);
}



2、源代码✌️

gitee
github

✋ 👐 ☝️ 👇 👈 👉 🙌 🙏 👆 👏 💪 🤘 🖕骚操作走起

举报

相关推荐

0 条评论