当前位置: 首页 > news >正文

VS2022+QT6.7+NetWork(TCP服务器多客户端助手)

目录

一、前言

二、代码详解

三、gitee完整项目下载


一、前言

实现的功能:

1、打开TCP服务器监听客户端连接

2、当有多个客户端连接服务器时,TCP服务器会将客户端放到子线程中去,同时在UI中更新,并记录每一个客户端的套接字、线程ID

3、客户端数据的接收,TCP服务器消息广播给客户端

4、选择性的将服务器中已连接的客户端踢出

准备工作:

您需要在VS项目中引用NetWork模块

程序运行效果如下:

二、代码详解

QtWidgetsApplication3.h

#pragma once#include <QtWidgets/QWidget>
#include "ui_QtWidgetsApplication3.h"
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QHostAddress>
#include <QByteArray>
#include <QThread>
#include <QStringListModel>
#include <QStandardItemModel>class QtWidgetsApplication3 : public QWidget
{Q_OBJECTpublic:QtWidgetsApplication3(QWidget* parent = nullptr);~QtWidgetsApplication3();QTcpSocket* tcpsocket;      //客户端对象套接字QTcpServer* tcpserver;      //服务器对象void open_connect_server(); //打开服务器void init();             //初始化void new_connect();      //新的客户端连接void kick_out();         //服务器踢下线void close_server();     //关闭服务器void send_data();        //发送数据void clear_data();       //清空数据QStringListModel* clientModel;  //用于ListView的模型QStandardItemModel* ItemModel = new QStandardItemModel(this); //存放所有连接的IP+线程QMap<QThread*, QTcpSocket*> threadMap; // 用于存储线程与连接信息的映射public slots:void socket_info(QThread*, QString, QTcpSocket*);void socket_on_disconnect(QThread*, QString, QTcpSocket*);void socket_readAll(QThread*, QString, QTcpSocket*, QByteArray);private:Ui::QtWidgetsApplication3Class ui;
};class ClientThread : public QObject
{Q_OBJECTpublic:ClientThread(QTcpSocket* socket, QObject* parent = 0);~ClientThread();QTcpSocket* tcpsocket;// 客户端套接字QThread* m_thread;    // 管理的子线程QString info;void run();
signals:void send_socket_info(QThread*, QString, QTcpSocket*);                // 发送客户端信息给UI更新void send_socket_on_disconnect(QThread*, QString, QTcpSocket*);       // 发送客户端断开信息给UI更新void send_socket_readAll(QThread*, QString, QTcpSocket*, QByteArray); // 发送客户端数据给UI更新public slots:void readAll();       // 获取客户端数据void on_disconnect(); // 断开连接
};

QtWidgetsApplication3.cpp

#include "QtWidgetsApplication3.h"QtWidgetsApplication3::QtWidgetsApplication3(QWidget* parent): QWidget(parent)
{ui.setupUi(this);//初始化init();//初始化模型并绑定到ListViewclientModel = new QStringListModel(this);ui.listView->setModel(clientModel); //打开服务器connect(ui.open_server, &QPushButton::clicked, this, &QtWidgetsApplication3::open_connect_server);//服务器踢线connect(ui.pushButton_2, &QPushButton::clicked, this, &QtWidgetsApplication3::kick_out);//关闭服务器connect(ui.close_server, &QPushButton::clicked, this, &QtWidgetsApplication3::close_server);//发送消息给客户端connect(ui.pushButton, &QPushButton::clicked, this, &QtWidgetsApplication3::send_data);//清除数据connect(ui.clear, &QPushButton::clicked, this, &QtWidgetsApplication3::clear_data);
}// 子线程的构造函数
ClientThread::ClientThread(QTcpSocket* socket, QObject* parent): QObject(parent), tcpsocket(socket), m_thread(new QThread(this))
{info = socket->peerAddress().toString() + ":" + QString::number(socket->peerPort()); //获取客户端IP+端口connect(socket, &QTcpSocket::readyRead, this, &ClientThread::readAll);          //客户端数据connect(socket, &QTcpSocket::disconnected, this, &ClientThread::on_disconnect); //客户端断开连接connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);   //客户端断开时自动安排socket对象删除connect(socket, &QTcpSocket::disconnected, m_thread, &QThread::quit);           //线程退出
}void ClientThread::run()
{emit send_socket_info(QThread::currentThread(), info, tcpsocket);
}// 子线程的析构函数
ClientThread::~ClientThread()
{}// 接收数据
void ClientThread::readAll()
{emit send_socket_readAll(QThread::currentThread(), info, tcpsocket, tcpsocket->readAll());
}// 客户端断开连接
void ClientThread::on_disconnect()
{emit send_socket_on_disconnect(QThread::currentThread(), info, tcpsocket);
}QtWidgetsApplication3::~QtWidgetsApplication3()
{}

TCP_server.cpp

#include "QtWidgetsApplication3.h"// 1、打开服务器
void QtWidgetsApplication3::open_connect_server()
{// 创建服务器tcpserver = new QTcpServer(this);// 当有新的客户端连接时,newConnection信号被触发connect(tcpserver, &QTcpServer::newConnection, this, &QtWidgetsApplication3::new_connect);// 监听指定IP+端口if (tcpserver->listen(QHostAddress((ui.comboBox->currentText())), ui.lineEdit_port->text().toUShort())){QString message = QString("正在监听:%1-%2").arg(ui.comboBox->currentText()).arg(ui.lineEdit_port->text());ui.textEdit->append(message);ui.open_server->setEnabled(false);ui.close_server->setEnabled(true);}
}// 2、客户端新连接
void QtWidgetsApplication3::new_connect()
{tcpsocket = tcpserver->nextPendingConnection();     //获取客户端套接字QThread* thread = new QThread();                    //创建线程ClientThread* client = new ClientThread(tcpsocket); //创建对象client->moveToThread(thread);                       //将客户端移动到线程中thread->start();                                    //启动线程//开启线程,在run中将线程号、客户端IP+端口、Socket套接字发送给UI更新connect(thread, &QThread::started, client, &ClientThread::run);//更新列表和模型,发送数据更新UIconnect(client, &ClientThread::send_socket_info, this, &QtWidgetsApplication3::socket_info);//客户端主动离线connect(client, &ClientThread::send_socket_on_disconnect, this, &QtWidgetsApplication3::socket_on_disconnect);//客户端数据接收connect(client, &ClientThread::send_socket_readAll, this, &QtWidgetsApplication3::socket_readAll);
}// 3、关闭服务器
void QtWidgetsApplication3::close_server()
{tcpserver->deleteLater();           // 稍后删除自己tcpserver->close();                 // 关闭服务器// 1. 清理所有线程和Socket连接for (auto thread : threadMap.keys()) {QTcpSocket* socket = threadMap[thread];if (socket) {socket->abort(); // 断开连接socket->deleteLater(); // 释放Socket资源}if (thread) {thread->quit(); // 退出线程thread->wait(); // 等待线程结束thread->deleteLater(); // 释放线程资源}}// 2. 清空映射表threadMap.clear();// 3. 清空ListView的数据模型if (ItemModel) {ItemModel->clear();}// 4. 在textEdit中显示服务器关闭信息ui.textEdit->append("服务器已关闭,所有连接已清理");ui.open_server->setEnabled(true); // 启用打开服务器按钮ui.close_server->setEnabled(false); // 禁用关闭服务器按钮
}// 4、更新列表和模型
void QtWidgetsApplication3::socket_info(QThread* thread,QString info, QTcpSocket* tcpsocket)
{threadMap[thread] = tcpsocket; // 保存线程和IPQString threadStr = QString("IP: %1 - 线程: %2").arg(info).arg((quintptr)thread, 0, 16);QStandardItem* item = new QStandardItem(threadStr);item->setCheckable(true);         // 设置可勾选属性item->setCheckState(Qt::Checked); // 默认勾选ItemModel->appendRow(item);ui.listView->setModel(ItemModel); 
}// 5、踢人
void QtWidgetsApplication3::kick_out()
{// 遍历所有项,检查哪些被选中for (int i = 0; i < ItemModel->rowCount(); ++i) {QStandardItem* item = ItemModel->item(i);if (item && item->checkState() == Qt::Checked) {// 解析线程IDQString threadStr = item->text();QString threadIdStr = threadStr.split("线程: ").last();bool ok;quintptr threadId = threadIdStr.toULongLong(&ok, 16);if (ok) {// 查找对应的线程和socketQThread* targetThread = nullptr;QTcpSocket* targetSocket = nullptr;for (auto thread : threadMap.keys()) {if ((quintptr)thread == threadId) {targetThread = thread;targetSocket = threadMap[thread];break;}}if (targetSocket) {targetSocket->abort(); // 立即关闭连接,丢弃所有待发送数据ui.textEdit->append(QString("已断开连接,线程ID: %1").arg(threadIdStr));// 清理线程if (targetThread) {targetThread->quit();targetThread->wait();targetThread->deleteLater(); //根据需要决定是否删除线程}// 从模型中移除该项ItemModel->removeRow(i);// 从映射中移除threadMap.remove(targetThread);i--; // 因为移除了一行,索引需要调整}}}}
}// 6、客户端主动断开连接
void QtWidgetsApplication3::socket_on_disconnect(QThread* thread, QString info, QTcpSocket* tcpsocket)
{// 查找对应的线程QThread* targetThread = nullptr;for (auto thread : threadMap.keys()) {if (threadMap[thread] == tcpsocket) {targetThread = thread;break;}}if (targetThread) {// 从列表中查找并移除对应的项QString threadIdStr = QString("%1").arg((quintptr)targetThread, 0, 16);for (int i = 0; i < ItemModel->rowCount(); ++i) {QStandardItem* item = ItemModel->item(i);if (item && item->text().contains(threadIdStr)) {ItemModel->removeRow(i);break;}}// 清理线程targetThread->quit();targetThread->wait();targetThread->deleteLater();// 从映射表中移除threadMap.remove(targetThread);// 在textEdit中显示信息ui.textEdit->append(QString("客户端 %1 主动断开连接,线程ID: %2").arg(info).arg(threadIdStr));}
}// 7、接收数据
void QtWidgetsApplication3::socket_readAll(QThread* thread, QString info, QTcpSocket* tcpsocket, QByteArray data)
{// 查找对应的客户端信息(IP和线程ID)QString threadIdStr;QThread* targetThread = nullptr;// 从映射表中查找线程for (auto thread : threadMap.keys()) {if (threadMap[thread] == tcpsocket) {targetThread = thread;threadIdStr = QString("%1").arg((quintptr)thread, 0, 16);break;}}// 显示接收信息到textEditif (!info.isEmpty() && !threadIdStr.isEmpty()) {ui.textEdit->append(QString("[接收] 客户端 %1 (线程ID: %2): %3").arg(info).arg(threadIdStr).arg(QString(data)));}
}// 8、发送数据
void QtWidgetsApplication3::send_data()
{// 检查是否有客户端连接if (threadMap.isEmpty()) {ui.textEdit->append("[广播失败] 没有任何客户端连接");return;}QString sendData=ui.lineEdit->text();foreach(QTcpSocket * socket, threadMap.values()) {if (socket && socket->state() == QTcpSocket::ConnectedState) {socket->write(sendData.toUtf8());socket->flush(); //立即发送}}ui.textEdit->append(QString("[广播] 所有客户端"));
}// 9、初始化
void  QtWidgetsApplication3::init()
{// 默认禁用关闭服务器按钮ui.close_server->setEnabled(false); // 获取本机所有IP地址,更新到comboBoxQList<QHostAddress> ipAddresses = QNetworkInterface::allAddresses();for (const QHostAddress& address : ipAddresses) {if (address.protocol() == QAbstractSocket::IPv4Protocol&& address != QHostAddress::LocalHost) {ui.comboBox->addItem(address.toString());}}
}// 10、清除数据
void QtWidgetsApplication3::clear_data()
{ui.textEdit->clear();
}

三、gitee完整项目下载

https://gitee.com/zjq11223344/qt-widgets-application3https://gitee.com/zjq11223344/qt-widgets-application3

http://www.dtcms.com/a/357107.html

相关文章:

  • Rust 登堂 之 深入Rust 类型(六)
  • 如何打造团队协作型 IP,而非单人依赖型?
  • BugKu Web渗透之file_get_contents
  • Kotlin中回调函数的使用示例
  • Git-Git和TortoiseGit的安装以及使用
  • 云渲染云推流助力WebGL应用网页端无负担推流,摆脱终端加载缓慢问题
  • 无恶意软件勒索:Storm-0501如何转向云原生攻击
  • Linux829 shell:expect interact “ “ set
  • 知识卡片html5动态网页源码
  • CRYPT32!CryptMsgUpdate函数分析之CRYPT32!PkiAsn1Decode函数的作用是得到pci
  • ros2--topic/话题--接口
  • tauri打包失败
  • 太阳光模拟器在卫星研发与测试中的应用
  • wav音频转C语言样点数组
  • 嵌入式Linux设备树驱动开发 - dtsof驱动
  • shell学习(二)
  • Sharding-JDBC 使用方法
  • 为什么不能创建泛型数组?
  • C++并发编程-17. 线程安全的链表
  • Unity EventTrigger 动态添加事件
  • flume事务机制详解:保障数据可靠性的核心逻辑
  • 项目中为什么使用SpringBoot?
  • 晨控CK-FR102ANS与欧姆龙NX系列PLC配置EtherNet/IP通讯连接手册
  • 如何规划一年、三年、五年的IP发展路线图?
  • Android 端 QGroundControl 控制 PC 端Gazebo Sim 仿真无人机
  • 龙迅#LT7642GX适用于4路HDMI2.1/DP/TPYE-C转HDMI+LVDS/MIPI混合开关应用,分辨率高达8K30HZ !
  • ADFS 和 OAuth 的区别
  • 第三届机械工程与先进制造智能化技术研讨会(MEAMIT2025)
  • 打造企业内部的“技术桥梁”:超级用户机制如何助力制造企业高效运维
  • “聚势同行・创赢未来”淮南高新区科技型企业沙龙——2025大数据企业专场成功举办