Qt/C++ 实现文件双向传输:从客户端到服务端,再从服务端到客户端

Qt/cpp实现的客户端往服务端传输文件、服务端往客户端传输文件,可监测传输状态,可控制传输。 源码: 使用Qt5.6.1_MinGW编译通过。 c.37

在很多网络应用场景中,文件的双向传输,也就是客户端往服务端传输文件以及服务端往客户端传输文件,并且能够监测传输状态和对传输进行控制,是非常常见的需求。今天就来聊聊如何使用 Qt/C++ 来实现这一功能。

一、环境说明

本文的代码使用 Qt5.6.1_MinGW 编译通过,确保你也配置好了相应的开发环境,以便顺利运行和理解代码。

二、客户端往服务端传输文件

1. 建立连接

首先,我们需要在客户端和服务端之间建立一个 TCP 连接。在 Qt 中,可以使用 QTcpSocket 来实现。

cpp 复制代码
QTcpSocket *socket = new QTcpSocket(this);
socket->connectToHost(QHostAddress::LocalHost, 1234);
if(socket->waitForConnected(5000)) {
    qDebug() << "Connected to server!";
} else {
    qDebug() << "Could not connect: " << socket->errorString();
}

这里我们创建了一个 QTcpSocket 对象,并尝试连接到本地主机的 1234 端口。waitForConnected 函数会阻塞等待,直到连接建立或者超时,这里设置的超时时间是 5 秒。如果连接成功,会输出连接成功的信息,否则输出错误信息。

2. 文件传输

接下来进行文件传输。假设我们要传输的文件是 test.txt

cpp 复制代码
QFile file("test.txt");
if(!file.open(QIODevice::ReadOnly)) {
    qDebug() << "Could not open file";
    return;
}

qint64 totalBytes = file.size();
qint64 bytesWritten = 0;
qint64 bytesToWrite = totalBytes;

while(bytesToWrite > 0) {
    QByteArray buffer = file.read(qMin(bytesToWrite, (qint64)1024));
    qint64 written = socket->write(buffer);
    if(written == -1) {
        qDebug() << "Write error: " << socket->errorString();
        break;
    }
    bytesWritten += written;
    bytesToWrite -= written;
    qDebug() << "Transferred " << bytesWritten << " of " << totalBytes << " bytes";
}

file.close();
socket->close();

首先打开要传输的文件,如果打开失败则输出错误信息并返回。获取文件的总大小 totalBytes,然后循环读取文件内容并写入到 socket 中。每次读取最多 1024 字节的数据,写入成功后更新已传输字节数 bytesWritten 和剩余要传输字节数 bytesToWrite,并输出当前传输进度。传输完成后关闭文件和 socket。

3. 监测传输状态

为了监测传输状态,我们可以在传输过程中实时输出已传输字节数和总字节数,就像上面代码中那样。也可以使用信号槽机制,比如 QTcpSocketbytesWritten 信号。

cpp 复制代码
connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateTransferProgress(qint64)));

void MyClass::updateTransferProgress(qint64 bytes) {
    static qint64 totalBytes = 0;
    totalBytes += bytes;
    qDebug() << "Transferred " << totalBytes << " bytes so far";
}

这里将 bytesWritten 信号连接到自定义的槽函数 updateTransferProgress,在槽函数中累加已传输字节数并输出。

4. 控制传输

可以通过一些逻辑来控制传输,比如暂停和继续。我们可以在连接成功后设置一个标志位来表示传输状态。

cpp 复制代码
bool isPaused = false;

// 假设这里有一个暂停按钮的点击事件槽函数
void MyClass::onPauseButtonClicked() {
    isPaused = true;
    socket->pause();
}

// 继续按钮的点击事件槽函数
void MyClass::onResumeButtonClicked() {
    isPaused = false;
    socket->resume();
}

当点击暂停按钮时,设置 isPaused 为 true 并调用 socket->pause() 暂停传输;点击继续按钮时,设置 isPaused 为 false 并调用 socket->resume() 继续传输。

三、服务端往客户端传输文件

1. 监听连接

服务端需要监听客户端的连接请求,使用 QTcpServer 来实现。

cpp 复制代码
QTcpServer *server = new QTcpServer(this);
if(!server->listen(QHostAddress::LocalHost, 1234)) {
    qDebug() << "Could not start server: " << server->errorString();
    return;
}

connect(server, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));

创建一个 QTcpServer 对象并尝试监听本地主机的 1234 端口。如果监听失败,输出错误信息并返回。将 newConnection 信号连接到自定义的槽函数 handleNewConnection,当有新的客户端连接时会调用这个槽函数。

2. 文件传输

handleNewConnection 槽函数中进行文件传输。假设要传输的文件也是 test.txt

cpp 复制代码
void MyClass::handleNewConnection() {
    QTcpSocket *socket = server->nextPendingConnection();
    QFile file("test.txt");
    if(!file.open(QIODevice::ReadOnly)) {
        qDebug() << "Could not open file";
        socket->close();
        return;
    }

    qint64 totalBytes = file.size();
    qint64 bytesWritten = 0;
    qint64 bytesToWrite = totalBytes;

    while(bytesToWrite > 0) {
        QByteArray buffer = file.read(qMin(bytesToWrite, (qint64)1024));
        qint64 written = socket->write(buffer);
        if(written == -1) {
            qDebug() << "Write error: " << socket->errorString();
            break;
        }
        bytesWritten += written;
        bytesToWrite -= written;
        qDebug() << "Transferred " << bytesWritten << " of " << totalBytes << " bytes";
    }

    file.close();
    socket->close();
}

这里获取新连接的 socket,打开要传输的文件,然后和客户端往服务端传输文件类似,循环读取文件内容并写入到 socket 中,实时输出传输进度。传输完成后关闭文件和 socket。

3. 监测传输状态与控制传输

和客户端类似,服务端也可以通过信号槽机制监测传输状态,比如使用 bytesWritten 信号。控制传输的逻辑也可以类似实现,设置标志位并通过相应的函数来暂停和继续传输。

通过以上步骤,我们就实现了 Qt/C++ 客户端往服务端传输文件、服务端往客户端传输文件,并且能够监测传输状态和控制传输的功能。希望这篇文章对你在网络文件传输相关的开发中有帮助。

相关推荐
Chan161 小时前
微服务 - Higress网关
java·spring boot·微服务·云原生·面试·架构·intellij-idea
没有bug.的程序员1 小时前
Serverless 架构深度解析:FaaS/BaaS、冷启动困境与场景适配指南
云原生·架构·serverless·架构设计·冷启动·baas·faas
一条咸鱼_SaltyFish3 小时前
[Day13] 微服务架构下的共享基础库设计:contract-common 模块实践
开发语言·人工智能·微服务·云原生·架构·ai编程
原神启动13 小时前
K8S(七)—— Kubernetes Pod 基础概念与实战配置
云原生·容器·kubernetes
牛奔4 小时前
Docker 容器无法停止的排障与解决全过程
运维·docker·云原生·容器·eureka
牛奔14 小时前
Docker Compose 两种安装与使用方式详解(适用于 Docker 19.03 版本)
运维·docker·云原生·容器·eureka
木童66215 小时前
Kubernetes 操作管理完全指南:从陈述式到声明式,覆盖全生命周期
云原生·容器·kubernetes
不想画图16 小时前
Kubernetes(三)——组网概念和基础操作指令
云原生·容器·kubernetes
Fortune_yangyang19 小时前
Kubernetes 操作管理
云原生·容器·kubernetes
放寒假脚后跟v1 天前
Pod 的 YAML 文件中 exitCode 字段的具体含义、不同取值代表的场景
运维·云原生·容器·kubernetes·k8s