首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Qt | TCP服务器实现QTcpServer,使用线程管理客户端套接字

Qt | TCP服务器实现QTcpServer,使用线程管理客户端套接字

原创
作者头像
Qt历险记
发布2024-12-01 11:39:18
发布2024-12-01 11:39:18
2.9K00
代码可运行
举报
文章被收录于专栏:Qt6 研发工程师Qt6 研发工程师
运行总次数:0
代码可运行

点击上方"蓝字"关注我们

01、QTcpServer

>>>QTcpServer 是 Qt 网络模块中的一个类,用于实现TCP服务器。它允许创建一个服务器,可以接受来自客户端的连接。QTcpServer 是事件驱动的,这意味着它将通过信号和槽机制处理网络事件。 常用函数

  1. 构造函数
    • QTcpServer(QObject *parent = nullptr): 创建一个 QTcpServer 对象。
  2. 启动服务器
    • bool listen(const QHostAddress &address, quint16 port): 在指定的地址和端口上监听传入的连接。返回值表示监听是否成功。
    • bool isListening() const: 检查服务器是否正在监听。
  3. 停止服务器
    • void close(): 停止服务器,并关闭所有活动的连接。
  4. 获取新连接
    • QTcpSocket *nextPendingConnection(): 返回下一个待处理的客户端连接(如果有的话)。每次调用后,会将已处理的连接移除。
  5. 信号
    • void newConnection(): 当有新的连接请求时发出此信号。可以连接到一个槽以处理新连接。
  6. 其他功能
    • QList<QTcpSocket*> findChildren< QTcpSocket* >() const: 获取所有与服务器相关的活动的客户端连接。
    • int maxPendingConnections() const: 获取允许的最大挂起连接数。
  7. incomingConnection(qintptr socketDescriptor) 是 QTcpServer 类中的一个虚拟函数,用于处理新的客户端连接。当服务器接收到新的连接请求时,这个函数会被调用,并传递一个 socketDescriptor 参数,该参数是一个整型值,用于唯一标识新连接的套接字

02、QTcpSocket

>>>QTcpSocket 是 Qt 网络模块中的一个类,用于实现网络通信中的 TCP 客户端功能。通过 QTcpSocket,开发者可以方便地与服务器进行数据交换,实现网络应用。以下是 QTcpSocket 的一些主要特性和功能:

  1. 连接管理:能够建立与远程主机的 TCP 连接,并管理连接状态,例如连接、断开连接等。
  2. 数据传输:支持异步读写操作,能够发送和接收字节流数据。可以通过 write() 方法发送数据,通过 read() 或 readAll() 方法接收数据。
  3. 信号和槽QTcpSocket 提供了许多信号(例如 connected()disconnected()readyRead() 等),可以与 Qt 的信号与槽机制结合使用,实现事件驱动的通信。
  4. 错误处理:能够处理网络错误,使用 errorOccurred() 信号提示用户发生了什么错误,并提供获取错误信息的方法。
  5. 支持 SSL/TLS:如果需要安全的通信,QTcpSocket 可以与 QSslSocket 一起使用,支持加密的数据传输。
  6. 易于集成:可以与 Qt 的其他模块(如 GUI、数据库等)灵活结合,构建复杂的网络应用。

03、QThread

>>>

  • 线程管理QThread 提供了一种简单的方式来管理线程的生命周期,包括启动、停止和退出线程。
  • 信号与槽QThread 支持 Qt 的信号与槽机制,允许线程之间进行通信。
  • 对象移动:可以将 QObject 派生类的对象移动到线程中,从而使对象在不同的线程上下文中执行。
  • 事件循环QThread 支持事件循环,可以在独立线程中处理事件,如 GUI 更新或网络事件。

常用函数

  1. 构造函数
    • QThread(QObject *parent = nullptr):构造一个 QThread 对象。
  2. 启动与退出线程
    • void start():启动线程运行,调用 run() 方法。
    • void quit():请求线程退出事件循环。
    • void wait(unsigned long timeout = 0):等待线程结束,直到线程完全退出。
  3. 线程执行
    • virtual void run():重载此方法来定义线程执行的代码。
  4. 移动对象
    • void moveToThread(QThread *thread):将 QObject 派生的对象移动到指定线程中。
  5. 获取线程信息
    • QThread::currentThread():返回当前执行线程的指针。
    • bool isRunning() const:判断线程是否在运行状态。
    • bool isFinished() const:判断线程是否已完成执行。
  6. 信号
    • void finished():线程完成时发出此信号。
    • void started():线程启动时发出此信号。

04、实战源码

>>>

代码语言:javascript
代码运行次数:0
运行
复制
# 设置 CMake 的最低版本要求cmake_minimum_required(VERSION 3.16)​# 定义项目名称和支持的语言c++project(threadedfortuneserver LANGUAGES CXX)​# 如果没有定义安装目录,则设置默认安装目录if(NOT DEFINED INSTALL_EXAMPLESDIR)    set(INSTALL_EXAMPLESDIR "examples")endif()​# 设置安装示例的子目录set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/network/threadedfortuneserver")​# 查找所需的 Qt 组件find_package(Qt6 REQUIRED COMPONENTS Core Gui Network Widgets)​# 设置 Qt 项目的标准配置qt_standard_project_setup()​# 添加可执行文件,指定源文件qt_add_executable(threadedfortuneserver    dialog.cpp dialog.h            # 对话框相关的源文件    fortuneserver.cpp fortuneserver.h # 幸运服务器相关的源文件    fortunethread.cpp fortunethread.h # 幸运线程相关的源文件    main.cpp                       # 主程序文件)​# 设置目标属性set_target_properties(threadedfortuneserver PROPERTIES    WIN32_EXECUTABLE TRUE        # 在 Windows 系统下创建 Windows 可执行文件    MACOSX_BUNDLE TRUE           # 在 MacOS 系统下创建应用程序包)​# 链接 Qt 库target_link_libraries(threadedfortuneserver PRIVATE    Qt6::Core                   # 链接 Qt Core 模块    Qt6::Gui                    # 链接 Qt GUI 模块    Qt6::Network                # 链接 Qt 网络模块    Qt6::Widgets                # 链接 Qt Widgets 模块)​# 安装目标install(TARGETS threadedfortuneserver    RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" # 可执行文件安装目录    BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"  # 应用程序包安装目录    LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" # 动态库安装目录)​

05、dialog.h

>>>dialog.h

代码语言:javascript
代码运行次数:0
运行
复制
#ifndef DIALOG_H // 检查 DIALOG_H 是否被定义#define DIALOG_H // 定义 DIALOG_H,以防止重复包含头文件​#include <QWidget> // 引入 Qt 的 QWidget 类#include "fortuneserver.h" // 引入自定义的 fortuneserver 头文件​QT_BEGIN_NAMESPACE // 开始命名空间class QLabel; // 前向声明 QLabel 类class QPushButton; // 前向声明 QPushButton 类QT_END_NAMESPACE // 结束命名空间​// Dialog 类,继承自 QWidgetclass Dialog : public QWidget{    Q_OBJECT // 声明该类是 Qt 的一个对象,支持信号和槽机制​public:    // 构造函数,接收一个 QWidget 指针作为父窗口,默认为 nullptr    Dialog(QWidget *parent = nullptr);​private:    QLabel *statusLabel; // 状态标签指针,用于显示状态信息    QPushButton *quitButton; // 退出按钮指针,用于退出应用    TcpServer server; // TcpServer 对象,用于处理 TCP 连接};​#endif // 结束 DIALOG_H 的条件编译​

06、dialog.cpp

>>>dialog.cpp

代码语言:javascript
代码运行次数:0
运行
复制
#include <QtWidgets> // 引入Qt Widgets模块#include <QtNetwork> // 引入Qt Network模块​#include <stdlib.h> // 引入标准库,以使用常见的功能​#include "dialog.h" // 引入Dialog类的头文件#include "fortuneserver.h" // 引入Fortune Server类的头文件​// Dialog类的构造函数Dialog::Dialog(QWidget *parent)    : QWidget(parent) // 调用基类QWidget的构造函数,并传递父窗口参数{    statusLabel = new QLabel; // 创建一个新的QLabel用于显示状态信息    statusLabel->setWordWrap(true); // 启用状态标签的自动换行    quitButton = new QPushButton(tr("Quit")); // 创建一个带有“Quit”文本的退出按钮    quitButton->setAutoDefault(false); // 禁用按钮的自动默认行为​    // 尝试启动服务器    if (!server.listen()) {        // 如果服务器启动失败,则显示严重错误信息        QMessageBox::critical(this, tr("Qt 历险记 qq交流:906134236"),                              tr("无法启动服务器: %1.")                                  .arg(server.errorString()));        close(); // 关闭对话框        return; // 退出构造函数    }​    QString ipAddress; // 变量用于存储IP地址    const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(); // 获取所有网络地址​    // 使用第一个非localhost的IPv4地址    for (const QHostAddress &entry : ipAddressesList) {        if (entry != QHostAddress::LocalHost && entry.toIPv4Address()) { // 检查是否为非localhost且是有效的IPv4            ipAddress = entry.toString(); // 存储IP地址            break; // 找到后退出循环        }    }​    // 如果没有找到,使用IPv4 localhost    if (ipAddress.isEmpty())        ipAddress = QHostAddress(QHostAddress::LocalHost).toString(); // 如果没有找到非localhost,则设置为localhost​    // 设置状态标签的文本,显示服务器信息    statusLabel->setText(tr("服务器正在运行\n\nIP: %1\n端口号: %2\n\n"                            "现在运行tcp服务器示例。")                             .arg(ipAddress).arg(server.serverPort()));​    // 将退出按钮的clicked信号连接到对话框的close槽    connect(quitButton, &QPushButton::clicked, this, &Dialog::close);​    // 创建一个水平布局用于按钮    QHBoxLayout *buttonLayout = new QHBoxLayout;    buttonLayout->addStretch(1); // 在按钮前添加可伸缩空间    buttonLayout->addWidget(quitButton); // 将退出按钮添加到布局中    buttonLayout->addStretch(1); // 在按钮后添加可伸缩空间​    // 创建一个垂直布局用于主窗口    QVBoxLayout *mainLayout = new QVBoxLayout;    mainLayout->addWidget(statusLabel); // 将状态标签添加到主布局中    mainLayout->addLayout(buttonLayout); // 将按钮布局添加到主布局中    setLayout(mainLayout); // 设置对话框的主布局    setWindowTitle(tr("Qt 历险记 qq交流:906134236")); // 设置窗口标题}​

07、fortuneserver.h

>>>fortuneserver.h

代码语言:javascript
代码运行次数:0
运行
复制
#ifndef FORTUNESERVER_H  // 确保该头文件只被包含一次#define FORTUNESERVER_H​#include <QStringList>  // 引入 QStringList,用于存储字符串列表#include <QTcpServer>   // 引入 QTcpServer 类,用于处理 TCP 服务器功能​// 定义 TcpServer 类,继承自 QTcpServerclass TcpServer : public QTcpServer{    Q_OBJECT  // 表示该类包含 Qt 的信号和槽机制​public:    // 构造函数,接收一个 QObject 指针作为父对象,默认为 nullptr    TcpServer(QObject *parent = nullptr);​protected:    // 重写 incomingConnection 方法,用于处理新的连接    void incomingConnection(qintptr socketDescriptor) override;​private:    QStringList fortunes;  // 用于存储“运势”字符串的列表};​#endif  // 结束条件编译指令​

08、fortuneserver.cpp

>>>fortuneserver.cpp

代码语言:javascript
代码运行次数:0
运行
复制
#include "fortuneserver.h"  // 引入自定义的 FortuneServer 头文件#include "fortunethread.h"  // 引入自定义的 FortuneThread 头文件​#include <QRandomGenerator>  // 引入 Qt 随机数生成器​#include <stdlib.h>         // 引入标准库头文件​// TcpServer 类构造函数TcpServer::TcpServer(QObject *parent)    : QTcpServer(parent)   // 调用基类 QTcpServer 的构造函数并传递父对象{    // 初始化 fortune 列表,存储不同的命运消息(字符串)    fortunes << tr("你过着狗一样的生活。别上家具。")             << tr("你必须考虑明天。")             << tr("你会被一声巨响惊讶。")             << tr("再过一个小时你会感到饿。")             << tr("你可能有邮件。")             << tr("你不能在不伤害永恒的情况下消磨时间。")             << tr("计算机并不聪明。它们只是认为自己聪明。");}​// 当有新的连接请求时的处理函数void TcpServer::incomingConnection(qintptr socketDescriptor){    // 随机选择一个命运消息    QString fortune = fortunes.at(QRandomGenerator::global()->bounded(fortunes.size()));​    // 创建一个新的 TcpSocketThread 线程对象,处理该连接    TcpSocketThread *thread = new TcpSocketThread(socketDescriptor, fortune, this);​    // 连接线程的 finished 信号和 deleteLater 槽,以在线程完成后自动删除对象    connect(thread, &TcpSocketThread::finished, thread, &TcpSocketThread::deleteLater);​    // 启动线程    thread->start();}​

09、fortunethread.h

>>>fortunethread.h

代码语言:javascript
代码运行次数:0
运行
复制
#ifndef FORTUNETHREAD_H     // 如果没有定义 FORTUNETHREAD_H#define FORTUNETHREAD_H     // 定义 FORTUNETHREAD_H​#include <QThread>          // 引入 QThread 头文件#include <QTcpSocket>       // 引入 QTcpSocket 头文件​// TcpSocketThread 类,继承自 QThreadclass TcpSocketThread : public QThread{    Q_OBJECT  // 宏,支持信号和槽机制​public:    // 构造函数,接受 socket 描述符和命运消息    TcpSocketThread(qintptr socketDescriptor, const QString &fortune, QObject *parent);​    // 重写 run() 方法,线程执行的入口    void run() override;​signals:    // 定义一个信号,用于报告错误    void error(QTcpSocket::SocketError socketError);​private:    qintptr socketDescriptor; // 保存 socket 描述符    QString text;             // 保存命运消息};​#endif // 结束条件​

10、fortunethread.cpp

>>>fortunethread.cpp

代码语言:javascript
代码运行次数:0
运行
复制
#include "fortunethread.h" // 引入自定义头文件 fortunethread.h​#include <QtNetwork> // 引入Qt网络模块​// TcpSocketThread 类的构造函数TcpSocketThread::TcpSocketThread(qintptr socketDescriptor, const QString &fortune, QObject *parent)    : QThread(parent), socketDescriptor(socketDescriptor), text(fortune){    // 初始化线程,保存套接字描述符和要发送的文本信息}​// 重写 run() 方法,线程执行的主要逻辑void TcpSocketThread::run(){    QTcpSocket tcpSocket; // 创建 TCP 套接字对象​    // 设置套接字描述符,如果失败则发出错误信号并返回    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {        emit error(tcpSocket.error());        return;    }​    QByteArray block; // 创建一个 QByteArray 用于存储要发送的数据    QDataStream out(&block, QIODevice::WriteOnly); // 创建数据流对象,使用只写模式    out.setVersion(QDataStream::Qt_6_5); // 设置数据流版本    out << text; // 将文本信息写入数据流​    tcpSocket.write(block); // 将数据块写入套接字    tcpSocket.disconnectFromHost(); // 断开与主机的连接    tcpSocket.waitForDisconnected(); // 等待断开连接}​

11、main.cpp

>>>

代码语言:javascript
代码运行次数:0
运行
复制
#include <QApplication>#include <QtCore>​#include <stdlib.h>​#include "dialog.h"​int main(int argc, char *argv[]){    QApplication app(argc, argv);    Dialog dialog;    dialog.show();    return app.exec();}​

12、推荐

>>>23种设计模式一网打尽。 项目源码获取,记得转存慢慢看。 通过网盘分享的文件:threadedfortuneserver 链接: https://pan.baidu.com/s/1vy4w2RWfQBBYoffurzZZiw?pwd=hr99 提取码: hr99

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 点击上方"蓝字"关注我们
  • 01、QTcpServer
  • 02、QTcpSocket
  • 03、QThread
  • 04、实战源码
  • 05、dialog.h
  • 06、dialog.cpp
  • 07、fortuneserver.h
  • 08、fortuneserver.cpp
  • 09、fortunethread.h
  • 10、fortunethread.cpp
  • 11、main.cpp
  • 12、推荐
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档