Qt网络编程-TcpClient入门Demo(1)🔺
1、TCP Client🔗
使用QTcpSocket实现TCP Client,十分详细的入门Demo |
- 支持打开多个TCP Client窗口;👍
- 可选择是否以16进制字符串形式显示发送、接收的数据;👍
- 自动统计发送数据的总字节大小、接收数据的总字节大小;👌
- 判断TCP Socket状态变化;✌️
- 判断TCP Socket各类异常状态;✋
- 详细说明close、disconnectFromHost、abort三种断开连接的方式和优缺点; 👐
- 代码注释详细,便于学习阅读。 👇
1.1 示例代码结构👀

1.2 TCPClient流程图🔀

1.3 实现效果🙉

1.4 关键信号🎻
信号 | 说明 |
---|
connected | QTcpSocket连接成功后发出此信号 |
disconnected | QTcpSocket断开连接后发出此信号 |
stateChanged | QTcpSocket状态发生改变后发出此信号(不必要) |
readyRead | 有可读数据时发出此信号 |
errorOccurred | QTcpSocket发生异常时发出此信号,qt5.15 后error()信号已经弃用 |
1.5 关键函数 💍
函数名 | 说明 |
---|
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.6 关于connectToHost内存泄漏问题🐷
- 在网络有很多博客都在说
connectToHost
会有内存泄漏问题,必须控制connectToHost的使用次数,连接接近1000时才会有明显的影响 (这些博客还自己测试过,通过任务管理器看出内存泄漏,不知道他是怎么看出来的)😬; - 这么明显的内存泄露我相信Qt开发者不可能不知道,所以对于这个问题最好自己测试一下;👌
- 一个定时器,间隔100毫秒调用一次下列代码中的
on_but_connect_clicked()
函数,我一共调用了十万次,通过 Visual Studio 的进程内存窗口看是否内存泄漏, 并没有出现内存泄漏,可以放心使用; - 对于网上说有内存泄漏我有几个猜测:
- 在Qt的 远古版本中有这个问题,新版本中被修复了;
- 最开始提出
connectToHost
有内存泄漏的人代码写出来,然后后面一堆人跟着抄作业,抄作业也抄错了。

- 对于使用任务管理器看内存泄漏一事也顺手测一下,并不能看出来 👊


1.7 主要代码🌈
- 在
.pro
或.pri
文件中添加QT += network
#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
if(m_tcpClient->state() != QAbstractSocket::UnconnectedState)
{
m_tcpClient->abort();
}
delete ui;
}
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))
connect(m_tcpClient, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
this, &TCPClient::on_errorOccurred);
#else
connect(m_tcpClient, &QTcpSocket::errorOccurred, this, &TCPClient::on_errorOccurred);
#endif
}
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());
}
void TCPClient::on_disconnected()
{
ui->but_connect->setText("连接");
qInfo() <<QString("断开连接:%1 %2").arg(m_tcpClient->peerName()).arg(m_tcpClient->peerPort());
}
void TCPClient::on_stateChanged(QAbstractSocket::SocketState state)
{
qInfo() << "状态改变:"<< state;
}
void TCPClient::on_errorOccurred(QAbstractSocket::SocketError error)
{
qWarning() << "出现异常:" <<error;
}
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);
}
}
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
m_tcpClient->abort();
#else
#if 0
m_tcpClient->disconnectFromHost();
#else
m_tcpClient->close();
#endif
qDebug() <<"IO状态:" << m_tcpClient->isOpen();
if (m_tcpClient->state() == QAbstractSocket::UnconnectedState)
{
if(m_tcpClient->waitForDisconnected(1000))
{
qInfo() <<"关闭成功!";
}
else
{
qWarning() << "关闭失败:"<<m_tcpClient->error();
}
}
#endif
}
}
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();
#else
QByteArray arr = str.toUtf8();
#endif
if(ui->check_hexSend->isChecked())
{
arr = QByteArray::fromHex(arr);
}
qint64 len = m_tcpClient->write(arr);
if(len < 0)
{
qWarning() <<"发送失败!";
}
ui->spin_send->setValue(ui->spin_send->value() + len);
}
void TCPClient::on_but_clearRecv_clicked()
{
ui->text_recv->clear();
ui->spin_recv->setValue(0);
}
void TCPClient::on_but_clearSend_clicked()
{
ui->text_send->clear();
ui->spin_send->setValue(0);
}
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);
}
void TCPClient::on_check_hexRecv_clicked(bool checked)
{
QString value;
if(checked)
{
value = ui->text_recv->toPlainText().toUtf8().toHex(' ');
}
else
{
value = QByteArray::fromHex(ui->text_recv->toPlainText().toUtf8());
}
ui->text_recv->setText(value);
}
2、源代码✌️
gitee
github
✋ 👐 ☝️ 👇 👈 👉 🙌 🙏 👆 👏 💪 🤘 🖕骚操作走起