Qt中TCP Socket的实现

 更新时间:2026年02月06日 08:52:26   作者:枫叶丹4  
Qt框架通过QTcpServer和QTcpSocket类提供跨平台的TCP通信能力,本文就来介绍一下Qt中TCP Socket的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1 -> 概述

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接、可靠、基于字节流的传输层通信协议。在 Qt 框架中,TCP 网络编程主要通过 QTcpServerQTcpSocket 两个核心类来实现,它们封装了底层 Socket 的复杂性,提供了面向对象、事件驱动的高级 API,大大简化了网络应用的开发过程。

Qt 的网络模块是跨平台的,这意味着使用 Qt 编写的 TCP 程序可以在 Windows、Linux、macOS 等操作系统上运行而无需修改代码,这对于需要部署在多种环境下的应用程序来说是一个巨大的优势。

在 Qt 中进行 TCP 编程,本质上是在构建客户端-服务器模型。服务器负责监听指定端口、接受客户端连接请求并与客户端进行数据交换;客户端则主动发起连接,与服务器建立通信链路并进行数据传输。Qt 的信号与槽机制非常适合处理网络事件,例如连接建立、数据到达、连接断开等,使得程序逻辑清晰、易于维护。

2 -> 核心 API 详解

2.1 -> QTcpServer

QTcpServer 类用于创建一个 TCP 服务器,其主要职责是监听指定 IP 地址和端口,接受传入的连接请求,并为每个连接创建一个独立的 QTcpSocket 对象来处理后续通信。

2.1.1 -> 关键方法

  • bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)
    启动服务器监听。

    • address:监听的 IP 地址,QHostAddress::Any 表示监听所有网络接口。
    • port:端口号,如果设为 0,系统会自动分配一个可用端口。
    • 返回值:成功监听返回 true,否则返回 false(可通过 errorString() 获取错误信息)。
  • QTcpSocket *nextPendingConnection()
    获取下一个等待处理的客户端连接。
    该方法返回一个已建立连接的 QTcpSocket 对象,该对象由 QTcpServer 内部创建并管理,用于后续与对应客户端的数据交换。
    注意:每次调用都应检查返回值是否为 nullptr。

2.1.2 -> 关键信号

  • void newConnection()
    当有新的客户端连接成功时触发此信号。通常在此信号的槽函数中调用 nextPendingConnection() 获取新连接并进行初始化设置(如连接信号槽、保存 socket 对象等)。

2.1.3 -> 工作流程简述

  1. 创建 QTcpServer 对象。
  2. 调用 listen() 绑定端口并开始监听。
  3. 连接 newConnection() 信号到自定义槽函数。
  4. 在槽函数中通过 nextPendingConnection() 获取客户端 socket。
  5. 为获取到的 socket 连接相关信号(如 readyRead、disconnected),实现业务逻辑。

2.2 -> QTcpSocket

QTcpSocket 类代表一个 TCP 连接,既可用于客户端发起连接,也可用于服务器端处理某个具体客户端的通信。它继承自 QAbstractSocket,并间接继承自 QIODevice,因此可以像文件一样进行读写操作。

2.2.1 -> 关键方法

  • void connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite)
    (客户端使用)连接到指定的服务器。

    • hostName:主机名或 IP 地址。
    • port:目标端口。
    • openMode:打开模式,通常为 ReadWrite。
  • qint64 read(char *data, qint64 maxSize) / QByteArray readAll() / QByteArray read(qint64 maxSize)
    从接收缓冲区读取数据。readAll() 会读取所有当前可用的数据,返回 QByteArray 对象。

  • qint64 write(const char *data, qint64 size) / qint64 write(const QByteArray &data)
    向连接中写入数据。数据会被放入发送缓冲区,由 Qt 在后台异步发送。

  • void disconnectFromHost()
    主动断开连接。断开过程是异步的,可配合 disconnected() 信号使用。

  • void abort()
    立即中止连接,丢弃所有未发送或未接收的数据。

2.2.2 -> 关键信号

  • void connected()
    成功连接到远程主机后触发。

  • void disconnected()
    连接断开时触发(可能是远程主机关闭、网络故障或主动断开)。

  • void readyRead()
    当有新的数据到达并可供读取时触发。由于 TCP 是流式协议,数据可能分多次到达,通常在此信号的槽函数中循环读取直到 bytesAvailable() 为 0。

  • void bytesWritten(qint64 bytes)
    当部分数据被成功写入底层 socket 时触发,可用于实现发送进度指示或流量控制。

2.2.3 -> 关键属性与状态

  • state()
    返回 socket 的当前状态(如 UnconnectedState、ConnectedState 等)。

  • error() 与 errorString()
    获取最后一次发生的错误代码及描述。

  • peerAddress() 与 peerPort()
    获取远程对端的 IP 地址和端口号(在连接建立后有效)。

2.3 -> QByteArray 与 QString 的转换

由于网络传输的是字节流,而 Qt 程序中常使用 QString 处理文本,因此两者间的转换非常常见:

  • QString → QByteArray
    QString str = "Hello";
    QByteArray data = str.toUtf8(); // 常用 UTF-8 编码
    
  • QByteArray → QString
    QByteArray data = ...;
    QString str = QString::fromUtf8(data);
    

3 -> 通信流程概述

3.1 -> 服务器端典型流程

  1. 创建 QTcpServer,监听指定端口。
  2. newConnection() 槽中接受新连接,获取 QTcpSocket
  3. 为该 socket 连接 readyRead() 信号,在槽函数中读取请求数据,处理业务,并回复响应。
  4. 连接 disconnected() 信号,在客户端断开时进行资源清理(如调用 deleteLater() 删除 socket 对象)。

3.2 -> 客户端典型流程

  1. 创建 QTcpSocket,调用 connectToHost() 连接服务器。
  2. 连接 connected() 信号,确认连接成功。
  3. 连接 readyRead() 信号,接收服务器返回的数据。
  4. 使用 write() 发送请求数据。
  5. 在适当时机断开连接或处理异常断开。

4 -> 代码示例

4.1 -> 回显客户端

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QMessageBox>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 1. 创建 QTcpServer 的实例
    socket = new QTcpSocket(this);

    // 2. 设置标题
    this->setWindowTitle("客户端");

    // 3. 和服务器建立连接
    socket->connectToHost("127.0.0.1", 9090);

    // 4. 连接信号槽, 处理响应
    connect(socket, &QTcpSocket::readyRead, this, [=](){
        // a) 读取出响应内容
        QString response = socket->readAll();

        // b) 把响应内容显示到界面上
        ui->listWidget->addItem("服务器说: " + response);

    });

    // 5. 等待连接建立的结果. 确认是否连接成功
    bool ret = socket->waitForConnected();
    if (!ret)
    {
        QMessageBox::critical(this, "连接服务器出错", socket->errorString());
        exit(1);
    }

}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    // 1. 获取到输入框中的内容
    const QString& text = ui->lineEdit->text();

    // 2. 发送数据给服务器
    socket->write(text.toUtf8());

    // 3. 把发的消息显示到界面上
    ui->listWidget->addItem("客户端说: " + text);

    // 4. 清空输入框的内容
    ui->lineEdit->setText("");

}

4.2 -> 回显服务端

widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QMessageBox>
#include <QTcpSocket>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 1. 创建 QTcpServer 的实例
    tcpServer = new QTcpServer(this);

    // 2. 设置标题
    this->setWindowTitle("服务器");

    // 3. 通过信号槽, 指定如何处理连接
    connect(tcpServer, &QTcpServer::newConnection, this, &Widget::processConnection);

    bool ret = tcpServer->listen(QHostAddress::Any, 9090);
    if (!ret)
    {
        QMessageBox::critical(this, "服务器启动失败!", tcpServer->errorString());
        exit(1);
    }

}

Widget::~Widget()
{
    delete ui;
}

void Widget::processConnection()
{
    // 1. 通过 tcpServer 拿到一个 socket 对象, 通过这个对象来和客户端进行通信
    QTcpSocket* clientSocket = tcpServer->nextPendingConnection();
    QString log = "[ " + clientSocket->peerAddress().toString() + ": " + QString::number(clientSocket->peerPort()) +
            " ] 客户端上线!";
    ui->listWidget->addItem(log);

    // 2. 通过信号槽, 来处理客户端发来请求的情况
    connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
        // a) 读取出请求数据. 此处 readAll 返回的是 QByteArray, 通过赋值转成 QString
        QString request = clientSocket->readAll();

        // b) 根据请求处理响应
        const QString& response = process(request);

        // c) 把响应写回到客户端
        clientSocket->write(response.toUtf8());

        // d) 把上述信息记录到日志中
        QString log = "[ " + clientSocket->peerAddress().toString() + ": " + QString::number(clientSocket->peerPort()) + " ] "
                + "req: " + request + ", " + "resp: " + response;
        ui->listWidget->addItem(log);

    });

    // 3. 通过信号槽, 来处理客户端断开连接的情况
    connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
        // a) 把断开连接的信息通过日志显示出来
        QString log = "[ " + clientSocket->peerAddress().toString() + ": " + QString::number(clientSocket->peerPort()) +
                " ] 客户端下线!";
        ui->listWidget->addItem(log);
        // b) 手动释放 clientSocket. 直接使用 delete 是下策, 使用 deleteLater 更加合适的.
        // delete clientSocket;
        clientSocket->deleteLater();
    });

}

// 此处写的是回显服务器
QString Widget::process(const QString request)
{

    return request;
}


5 -> 总结

Qt 的 TCP 网络编程模块通过 QTcpServerQTcpSocket 提供了高度封装、易于使用的 API,大大降低了网络应用程序的开发难度。其特点可总结如下:

  • 面向对象与事件驱动:通过信号与槽机制处理连接、数据接收、断开等事件,代码结构清晰。
  • 跨平台性:同一套代码可在主流操作系统上运行,无需关心底层 Socket API 差异。
  • 与 Qt 生态无缝集成:可方便地与 GUI、多线程、定时器、文件 IO 等其他 Qt 模块结合使用。
  • 高效的异步 IO:基于事件循环,无需阻塞线程即可处理多个并发连接,适合高性能服务器开发。
  • 良好的错误处理机制:提供详细的错误码和描述,便于调试和异常处理。

在实际开发中,除了掌握上述基础 API 外,还需注意资源管理(及时释放 socket)、协议设计(定义清晰的数据包格式以处理粘包/半包问题)、多线程处理(将耗时操作移至子线程以避免阻塞主循环)等高级主题。Qt 的网络模块为构建稳定、高效的 C++ 网络应用提供了坚实的基础,是开发跨平台客户端/服务器程序的优秀选择。

到此这篇关于Qt中TCP Socket的实现的文章就介绍到这了,更多相关Qt TCP Socket内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 简介C/C++预处理器的一些工作

    简介C/C++预处理器的一些工作

    这篇文章主要介绍了C/C++预处理器的一些工作,有助于理解编译器底层的工作流程,需要的朋友可以参考下
    2015-07-07
  • C++中栈结构建立与操作详细解析

    C++中栈结构建立与操作详细解析

    我们可以把栈理解成一个大仓库,放在仓库门口(栈顶)的货物会优先被取出,然后再取出里面的货物。而从数据的逻辑结构来看,栈结构起始就是一种线性结构
    2013-10-10
  • C语言中volatile关键字的作用及说明

    C语言中volatile关键字的作用及说明

    文中主要介绍了C语言中volatile关键字的含义和使用场景,volatile是一个类型修饰符,主要用来修饰被不同线程访问和修改的变量,它的作用是防止编译器对代码进行优化,确保每次直接读取原始内存地址的值
    2024-10-10
  • Qt出现假死冻结现象的原因及解决方法

    Qt出现假死冻结现象的原因及解决方法

    应用程序出现假死或冻结现象通常是由于一些常见问题所导致的,本文主要介绍了Qt出现假死冻结现象的原因及解决方法,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • c++ vector 常用函数示例解析

    c++ vector 常用函数示例解析

    这篇文章主要介绍了c++ vector 常用函数示例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • C++实现猜数字游戏

    C++实现猜数字游戏

    这篇文章主要为大家详细介绍了C++实现猜数字游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • C语言中计算正弦的相关函数总结

    C语言中计算正弦的相关函数总结

    这篇文章主要介绍了C语言中计算正弦的相关函数总结,包括正弦和双曲线正弦以及反正弦的函数,需要的朋友可以参考下
    2015-08-08
  • C语言中char*和char[]用法区别分析

    C语言中char*和char[]用法区别分析

    这篇文章主要介绍了C语言中char*和char[]用法区别,包括使用过程中的误区及注意点分析,需要的朋友可以参考下
    2014-09-09
  • 基于C语言字符串函数的一些使用心得

    基于C语言字符串函数的一些使用心得

    以下是对C语言中字符串函数的一些使用心得进行了详细的介绍,需要的朋友可以过来参考下
    2013-08-08
  • c语言调用汇编的方法

    c语言调用汇编的方法

    在此记录一下c调用汇编的方法,汇编使用的是AT&T语法。例子很简单,就是在给一个整数用汇编转换成二进制
    2013-11-11

最新评论