以下是使用 Qt 框架编写的一个类,包含登录远程文件服务器、发送文件和实时通知文件传输状态、进度信息的功能,同时保证所有文件传输在子线程中运行。
#include <QtCore>
#include <QtNetwork>
class RemoteFileClient : public QObject
{
Q_OBJECT
public:
explicit RemoteFileClient(QObject *parent = nullptr)
: QObject(parent)
{}
void login(const QString &username, const QString &password, const QString &serverAddress, int port)
{
// 创建 TCP 连接,并在连接成功后发送登录请求
tcpSocket.connectToHost(serverAddress, port);
if (tcpSocket.waitForConnected()) {
sendLoginRequest(username, password);
} else {
emit loginError("无法连接到服务器:" + tcpSocket.errorString());
}
}
void sendFile(const QString &localFilePath, const QString &remoteFilePath)
{
// 创建一个新线程,用于发送文件,并将其移动到该线程中执行
QThread *thread = new QThread;
FileSender *sender = new FileSender(localFilePath, remoteFilePath, &tcpSocket);
sender->moveToThread(thread);
// 连接信号和槽函数,实现实时通知文件传输状态、进度信息
connect(sender, &FileSender::finished, thread, &QThread::quit);
connect(sender, &FileSender::statusUpdate, this, &RemoteFileClient::sendStatusUpdate);
connect(sender, &FileSender::error, this, &RemoteFileClient::sendError);
// 启动子线程并开始文件传输
connect(thread, &QThread::started, sender, &FileSender::send);
thread->start();
}
signals:
void loginSuccess();
void loginError(const QString &errorMessage);
void sendStatusUpdate(const QString &status, int progress);
void sendError(const QString &errorMessage);
private slots:
void handleLoginResponse()
{
// 解析登录响应并发送信号
QByteArray responseData = tcpSocket.readAll();
if (responseData == "success") {
emit loginSuccess();
} else {
emit loginError("登录失败:" + responseData);
}
}
private:
QTcpSocket tcpSocket;
void sendLoginRequest(const QString &username, const QString &password)
{
// 发送登录请求,并在收到响应后处理
QString requestData = "login " + username + " " + password;
tcpSocket.write(requestData.toUtf8());
if (tcpSocket.waitForBytesWritten() && tcpSocket.waitForReadyRead()) {
handleLoginResponse();
} else {
emit loginError("登录超时或中断:" + tcpSocket.errorString());
}
}
};
class FileSender : public QObject
{
Q_OBJECT
public:
explicit FileSender(const QString &localFilePath, const QString &remoteFilePath, QTcpSocket *socket, QObject *parent = nullptr)
: QObject(parent),
localFile(localFilePath),
remoteFile(remoteFilePath),
tcpSocket(socket)
{}
signals:
void finished();
void statusUpdate(const QString &status, int progress);
void error(const QString &errorMessage);
public slots:
void send()
{
// 打开本地文件,并准备向服务器发送数据
QFile file(localFile);
if (!file.open(QIODevice::ReadOnly)) {
emit error("无法打开本地文件:" + file.errorString());
return;
}
// 发送文件名和大小,并在收到响应后开始发送文件内容
qint64 fileSize = file.size();
QString requestData = "send " + remoteFile + " " + QString::number(fileSize);
tcpSocket->write(requestData.toUtf8());
if (tcpSocket->waitForBytesWritten() && tcpSocket->waitForReadyRead()) {
QByteArray responseData = tcpSocket->readAll();
if (responseData == "ready") {
sendFileContent(&file, fileSize); // 开始发送文件内容
} else {
emit error("服务器拒绝接收文件:" + responseData);
}
} else {
emit error("请求超时或中断:" + tcpSocket->errorString());
}
}
private:
QString localFile, remoteFile;
QTcpSocket *tcpSocket;
void sendFileContent(QFile *file, qint64 fileSize)
{
// 定义缓冲区和每次读取的字节数
QByteArray buffer;
const int bufferSize = 1024 * 1024; // 每次 1MB
// 发送文件内容,并实时通知状态、进度信息
qint64 totalBytesSent = 0;
while (totalBytesSent < fileSize) {
buffer = file->read(qMin(fileSize - totalBytesSent, qint64(bufferSize)));
qint64 bytesSent = tcpSocket->write(buffer);
totalBytesSent += bytesSent;
int progressPercent = int(totalBytesSent * 100 / fileSize);
emit statusUpdate("正在发送", progressPercent);
}
// 关闭文件和套接字,并发送完成信号
file->close();
tcpSocket->waitForBytesWritten();
tcpSocket->close();
emit finished();
}
};