Qt网络编程-TcpServer入门Demo(2)👌
1、TCP Server🔗
使用QTcpServer实现TCP Server,十分详细的入门Demo |
- 支持打开多个TCP Server窗口;👍
- 支持同时连接多个TcpClient,将连接的TcpClient添加进通信列表;
- 支持自动移除通信列表中断开连接的TcpClient;
- 支持一对多进行数据通信,或通过在连接TcpClient列表中勾选需要通信的对象进行数据通信;
- 支持频繁断开连接大量的QTcpSocket并不存在内存泄漏;
- 可选择是否以16进制字符串形式显示发送、接收的数据;👍
- 自动统计发送数据的总字节大小、接收数据的总字节大小;👌
- 判断TCP Socket状态变化;✌️
- 判断TCP Server各类异常状态;✋
- 详细说明close、disconnectFromHost、abort三种断开连接的方式和优缺点; 👐
- 代码注释详细,便于学习阅读。 👇
- 注意:如果程序需要频繁断开连接,那就需要考虑内存泄漏问题
- QTcpServer存在一些内存泄漏问题,如果没有通过nextPendingConnection返回所有的的QTcpSocket并释放,将只有在QTcpServer释放时才会统一释放已连接的QTcpSocket;
- 如果程序需要频繁断开连接,解决这个内存泄漏问题就需要通过hasPendingConnections函数判断是否有未返回的已连接QTcpSocket,如果有就调用nextPendingConnection返回并释放。
1.1 TcpServer流程图🔀

1.2 实现效果🙉

1.3 关键信号🎻
信号 | 说明 |
---|
newConnection | 监听到新的客户端连接 |
acceptError | 当接受新连接导致错误时,会发出此信号 |
1.4 关键函数 💍
函数名 | 说明 |
---|
setMaxPendingConnections | 设置最大允许连接数,不设置的话默认为30 |
isListening | 判断是否正在监听连接 |
listen | 绑定服务器侦听地址和端口上的传入连接。如果端口为0,则会自动选择一个端口。(相当于bind + listen ) |
hasPendingConnections | 判断是否有已连接的QTcpSocket需要返回 |
nextPendingConnection | 返回已连接的QTcpSocket,如果有已连接的QTcpSocket没调用这个函数返回,将在QTcpServer释放时会自动统一释放(所以存在一定的内存泄漏问题) |
close | 关闭服务器。 服务器将不再监听传入连接 |
1.5 主要代码🌈
- 在
.pro
或.pri
文件中添加QT += network
#if defined(_MSC_VER) && (_MSC_VER >= 1600)
# pragma execution_character_set("utf-8")
#endif
#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QAbstractSocket>
#include <QDebug>
#include <QListWidgetItem>
#include <qhostinfo.h>
#include "share.h"
using namespace Share;
TCPServer::TCPServer(QWidget *parent) :
QWidget(parent),
ui(new Ui::TCPServer)
{
ui->setupUi(this);
this->setWindowTitle("TCP服务端Demo");
init();
connectSlots();
}
TCPServer::~TCPServer()
{
#ifdef QT_DEBUG
qDebug() <<"~TCPServer()";
#endif
delete ui;
}
void TCPServer::init()
{
m_tcpServer = new QTcpServer(this);
m_tcpServer->setMaxPendingConnections(30);
ui->line_localAddress->setText(getLocalIP());
}
void TCPServer::connectSlots()
{
connect(m_tcpServer, &QTcpServer::newConnection, this, &TCPServer::on_newConnection);
connect(m_tcpServer, &QTcpServer::acceptError, this, &TCPServer::on_acceptError);
}
void TCPServer::on_newConnection()
{
while (m_tcpServer->hasPendingConnections())
{
qDebug() <<m_tcpServer->hasPendingConnections();
QTcpSocket* tcpSocket = m_tcpServer->nextPendingConnection();
qDebug() <<m_tcpServer->hasPendingConnections();
if(tcpSocket)
{
m_tcpClients.append(tcpSocket);
}
connect(tcpSocket, &QTcpSocket::disconnected, this, &TCPServer::on_disconnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &TCPServer::on_readyRead);
QString strPeer = QString("%1 %2").arg(tcpSocket->peerAddress().toString()).arg(tcpSocket->peerPort());
strPeer.remove("::ffff:");
addPeer(strPeer);
}
}
void TCPServer::on_acceptError(QAbstractSocket::SocketError socketError)
{
qWarning() << QString("TcpServer异常:%1").arg(socketError);
}
void TCPServer::addPeer(const QString &peer)
{
QListWidgetItem *item = new QListWidgetItem(ui->listWidget);
QCheckBox* checkbox = new QCheckBox();
checkbox->setText(peer);
item->setSizeHint(QSize(0,20));
ui->listWidget->addItem(item);
ui->listWidget->setItemWidget(item, checkbox);
}
void TCPServer::on_but_connect_clicked()
{
if(!m_tcpServer) return;
if(!m_tcpServer->isListening())
{
bool ret = m_tcpServer->listen(QHostAddress::Any, ui->spin_localPort->value());
if(ret)
{
ui->but_connect->setText("停止");
}
else
{
qDebug() << QString("tcpServer绑定监听IP、端口失败:%1 %2").arg(m_tcpServer->errorString()).arg(m_tcpServer->serverError());
}
}
else
{
m_tcpServer->close();
ui->but_connect->setText("开始监听");
for(int i = m_tcpClients.count() - 1; i >= 0 ; i--)
{
m_tcpClients.at(i)->abort();
}
}
}
void TCPServer::on_disconnected()
{
for(int i = 0; i < m_tcpClients.count(); i++)
{
if(m_tcpClients.at(i)->state() != QAbstractSocket::ConnectedState)
{
disconnect(m_tcpClients.at(i), &QTcpSocket::disconnected, this, &TCPServer::on_disconnected);
disconnect(m_tcpClients.at(i), &QTcpSocket::readyRead, this, &TCPServer::on_readyRead);
m_tcpClients.takeAt(i)->deleteLater();
QListWidgetItem * item = ui->listWidget->item(i);
ui->listWidget->removeItemWidget(item);
delete item;
item = nullptr;
}
}
}
void TCPServer::on_readyRead()
{
for(int i = 0; i < ui->listWidget->count(); i++)
{
if(m_tcpClients.at(i)->bytesAvailable() <= 0) continue;;
QByteArray arr = m_tcpClients.at(i)->readAll();
QCheckBox* checkBox = (QCheckBox*)ui->listWidget->itemWidget(ui->listWidget->item(i));
if(checkBox->isChecked())
{
if(arr.count() <= 0)
{
continue;
}
ui->spin_recv->setValue(ui->spin_recv->value() + arr.count());
QString strPeer = checkBox->text();
if(ui->check_hexRecv->isChecked())
{
ui->text_recv->append(QString("[%1] ").arg(strPeer) + arr.toHex(' '));
}
else
{
ui->text_recv->append(QString("[%1] ").arg(strPeer) + arr);
}
}
}
}
void TCPServer::on_but_clearRecv_clicked()
{
ui->text_recv->clear();
ui->spin_recv->setValue(0);
}
void TCPServer::on_but_clearSend_clicked()
{
ui->text_send->clear();
ui->spin_send->setValue(0);
}
void TCPServer::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);
}
void TCPServer::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 TCPServer::on_but_send_clicked()
{
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 = sendData(arr);
if(len < 0)
{
qWarning() <<"发送失败!";
}
ui->spin_send->setValue(ui->spin_send->value() + len);
}
qint64 TCPServer::sendData(const QByteArray &data)
{
qint64 len = 0;
for(int i = 0; i < ui->listWidget->count(); i++)
{
QCheckBox* checkBox = (QCheckBox*)ui->listWidget->itemWidget(ui->listWidget->item(i));
if(checkBox->isChecked())
{
qint64 ret = m_tcpClients.at(i)->write(data);
if(ret > len)
{
len = ret;
}
}
}
return len;
}
2、源代码✌️
gitee
github
接着奏乐,接着舞🙋 👰 🙎 🙍 🙇 💑 💆 💇
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用