Qt实现TCP文件传输例子

  • (2022.4.4)完整的课设已经挂上 github:iTomxy/TomChat

  • (2018.6.8)收到评论说那个下载工程贵,去看了一下下载页…卧槽要 11 个积分(我当初应该没这么猖狂敢这么开价,毕竟主要是从 Qt 社区抄来的代码,记得好像是设了 3 分来着?)…
    不管怎么样,在此说明:工程的主要代码都在文章里贴出了,文末号称“完整代码”的下载链只是把整个工程打包了上传上去,没多什么东西的啊!
    如果真的需要整个工程,要不就直接联系我让我发给你吧。


参考

第38篇 网络(八)TCP(二)
QT TCP socket通信(二)

效果

  • 发送端
  1. 标签,提示状态信息
  2. 进度条,提示发送进度
  3. 选择按钮,单击弹出选文件对话框
  4. 发送按钮,单击开始发送
  • 接收端
  1. 标签,提示状态信息
  2. 进度条,提示发送进度
  3. 监听按钮,单击开始监听端口
  • 效果图
  1. 工程截图
    project
  2. 运行效果
    result

Notes

  • 发送端取消按钮未实现,忽略…
  • socket 向链接成功发送数据后,会发出bytesWritten(qint64)信号
  • QDataStream 设置版本(setVersion())时,发送端和接收端要设置成同一版本

主要代码

发送端

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QAbstractSocket>
class QByteArray;
class QFile;
class QString;
class QTcpSocket;

namespace Ui {
	class MainWindow;
}

class MainWindow : public QMainWindow
{
	Q_OBJECT

public:
	explicit MainWindow(QWidget *parent = 0);
	~MainWindow();

private slots:
	void start_transfer();
	void continue_transfer(qint64);
	void show_error(QAbstractSocket::SocketError);
	void on_selectBtn_clicked();
	void on_sendBtn_clicked();

private:
	Ui::MainWindow *ui;
	QTcpSocket *send;
	QFile *file;
	QString fileName;
	/* 总数据大小,已发送数据大小,剩余数据大小,每次发送数据块大小 */
	qint64 fileBytes, sentBytes, restBytes, loadBytes;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QByteArray>
#include <QDataStream>
#include <QFileDialog>
#include <QHostAddress>
#include <QIODevice>
#include <QString>
#include <QTcpSocket>

const quint16 PORT = 3333;
const qint64 LOADBYTES = 4 * 1024; // 4 kilo-byte
const int DATA_STREAM_VERSION = QDataStream::Qt_5_8;

MainWindow::MainWindow(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::MainWindow)
{
	ui->setupUi(this);
	send = new QTcpSocket(this);
	fileBytes = sentBytes = restBytes = 0;
	loadBytes = LOADBYTES;
	file = Q_NULLPTR;
	ui->sendProg->setValue(0); // 进度条置零
	ui->sendBtn->setEnabled(false); // 禁用发送按钮
	ui->cancelBtn->setEnabled(false);
	/* 连接已建立 -> 开始发数据 */
	connect(send, SIGNAL(connected()),
			this, SLOT(start_transfer()));
	/* 数据已发出 -> 继续发 */
	connect(send, SIGNAL(bytesWritten(qint64)),
			this, SLOT(continue_transfer(qint64)));
	/* socket出错 -> 错误处理 */
	connect(send, SIGNAL(error(QAbstractSocket::SocketError)),
			this, SLOT(show_error(QAbstractSocket::SocketError)));
}

MainWindow::~MainWindow()
{
	delete ui;
	delete send;
}

/*--- 点击选择按钮 ---*/
void MainWindow::on_selectBtn_clicked()
{
	/* 开文件选择窗选文件,返回带路径文件名 */
	fileName = QFileDialog::getOpenFileName(this);
	if(!fileName.isEmpty())
	{
		ui->stLabel->setText(
			QString("File %1 Opened!").arg(fileName));
		ui->sendBtn->setEnabled(true);
	}
	else
		ui->stLabel->setText(QString("*** FAIL OPENING FILE"));
}

/*--- 点击发送按钮 ---*/
void MainWindow::on_sendBtn_clicked()
{
	/* 发送连接请求 */
	send->connectToHost(QHostAddress::LocalHost, PORT);
	sentBytes = 0;
	ui->sendBtn->setEnabled(false);
	ui->cancelBtn->setEnabled(true);
	ui->stLabel->setText(QString("Linking..."));
}

/*--- 开始传送 ---*/
void MainWindow::start_transfer()
{
	file = new QFile(fileName);
	if(!file->open(QFile::ReadOnly))
	{
		ui->stLabel->setText(QString("*** FILE OPEN ERROR"));
		qDebug() << "*** start_transfer(): File-Open-Error";
		return;
	}

	fileBytes = file->size();
	ui->sendProg->setValue(0);
	ui->stLabel->setText(QString("Connection Established!"));

	QByteArray buf;
	QDataStream out(&buf, QIODevice::WriteOnly);
	out.setVersion(DATA_STREAM_VERSION);

	/* 无路径文件名 */
	QString sfName = fileName.right(fileName.size() -
			fileName.lastIndexOf('/') - 1);
	/* 首部 = 总大小 + 文件名长度 + 文件名 */
	out << qint64(0) << qint64(0) << sfName;
	/* 总大小加上首部的大小 */
	fileBytes += buf.size();
	ui->sendProg->setMaximum(fileBytes);
	/* 重写首部的前两个长度字段 */
	out.device()->seek(0);
	out << fileBytes << (qint64(buf.size()) - 2 * sizeof(qint64));
	/* 发送首部,计算剩余大小 */
	restBytes = fileBytes - send->write(buf);
}

/*--- 继续传输 ---*/
void MainWindow::continue_transfer(qint64 sentSize)
{
	sentBytes += sentSize;
	ui->sendProg->setValue(sentBytes);
	/* 还有数据要发 */
	if(restBytes > 0)
	{
		/* 从文件读数据 */
		QByteArray buf = file->read(qMin(loadBytes, restBytes));
		/* 发送 */
		restBytes -= send->write(buf);
	}
	else
		file->close();
	/* 全部发送完 */
	if(sentBytes == fileBytes)
	{
		send->close(); // 关socket
		fileName.clear(); // 清空文件名
		ui->stLabel->setText(QString("Finish sending!"));
	}
}

/*--- 出错处理 ---*/
void MainWindow::show_error(QAbstractSocket::SocketError)
{
	qDebug() << "*** Socket Error";
	send->close();
	ui->stLabel->setText(QString("*** SOCKET ERROR, RESEND LATER"));
	ui->sendBtn->setEnabled(true);
	ui->sendProg->reset(); // 进度条归零
	fileName.clear();
}

接收端

receivefile.h

#ifndef RECEVIEFILE_H
#define RECEVIEFILE_H

#include <QMainWindow>
#include <QAbstractSocket>
class QFile;
class QString;
class QTcpServer;
class QTcpSocket;

namespace Ui {
	class RecevieFile;
}

class RecevieFile : public QMainWindow
{
	Q_OBJECT

public:
	explicit RecevieFile(QWidget *parent = 0);
	~RecevieFile();

private slots:
	void accept_connect();
	void recevie_file();
	void show_error(QAbstractSocket::SocketError);
	void on_listenBtn_clicked();

private:
	Ui::RecevieFile *ui;
	QTcpServer *server;
	QTcpSocket *receive;
	QString fileName;
	QFile *file;
	/* 已接受数据,总数据,文件名长度 */
	qint64 gotBytes, fileBytes, nameSize;
};

#endif // RECEVIEFILE_H

receivefile.cpp

  • (2022.5.7)@qq_28885527 指出「对于一次性接收的文件不能传输」的问题,并给出修改方案,详见此评论
#include "receviefile.h"
#include "ui_receviefile.h"
#include <iostream>
#include <QDataStream>
#include <QFile>
#include <QHostAddress>
#include <QTcpServer>
#include <QTcpSocket>

const quint16 PORT = 3333;
const int DATA_STREAM_VERSION = QDataStream::Qt_5_8;

RecevieFile::RecevieFile(QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::RecevieFile)
{
	ui->setupUi(this);
	/* 进度条调零 */
	ui->recvProg->setValue(0);
	/* 启用监听按钮 */
	ui->listenBtn->setEnabled(true);
	fileBytes = gotBytes = nameSize = 0;
	file = Q_NULLPTR;
	receive = Q_NULLPTR;
	server = new QTcpServer(this);
	/* 连接请求 -> 接受连接 */
	connect(server, SIGNAL(newConnection()),
			this, SLOT(accept_connect()));
}

RecevieFile::~RecevieFile()
{
	delete ui;
	delete server;
}

/*--- 点击监听按钮 ---*/
void RecevieFile::on_listenBtn_clicked()
{
	/* 禁用监听按钮 */
	ui->listenBtn->setEnabled(false);
	if(!server->listen(QHostAddress::Any, PORT))
	{
		std::cerr << "*** Listen to Port Failed ***" << std::endl;
		qDebug() << server->errorString();
		close();
		ui->listenBtn->setEnabled(false);
		return;
	}
	ui->stLabel->setText(QString("Listing to Port %1").arg(PORT));
}

/*--- 接受连接请求 ---*/
void RecevieFile::accept_connect()
{
	receive = server->nextPendingConnection();
	/* 有数据到 -> 接受数据 */
	connect(receive, SIGNAL(readyRead()),
			this, SLOT(recevie_file()));
	/* socket出错 -> 出错处理 */
	connect(receive, SIGNAL(error(QAbstractSocket::SocketError)),
			this, SLOT(show_error(QAbstractSocket::SocketError)));
	ui->stLabel->setText(QString("Connection Established!"));
	gotBytes = 0;
	server->close();
}

/*--- 接收文件 ---*/
void RecevieFile::recevie_file()
{
	QDataStream in(receive);
	in.setVersion(DATA_STREAM_VERSION);

	/* 首部未接收/未接收完 */
	if(gotBytes <= 2 * sizeof(qint64))
	{
		if(!nameSize) // 前两个长度字段未接收
		{
			if(receive->bytesAvailable() >= 2 * sizeof(qint64))
			{
				in >> fileBytes >> nameSize;
				gotBytes += 2 * sizeof(qint64);
				ui->recvProg->setMaximum(fileBytes);
				ui->recvProg->setValue(gotBytes);
			}
			else // 数据不足,等下次
			   return;
		}
		else if(receive->bytesAvailable() >= nameSize)
		{
			in >> fileName;
			gotBytes += nameSize;
			ui->recvProg->setValue(gotBytes);
			std::cout << "--- File Name: "
					  << fileName.toStdString() << std::endl;
		}
		else // 数据不足文件名长度,等下次
			return;
	}

	/* 已读文件名、文件未打开 -> 尝试打开文件 */
	if(!fileName.isEmpty() && file == Q_NULLPTR)
	{
		file = new QFile(fileName);
		if(!file->open(QFile::WriteOnly)) // 打开失败
		{
			std::cerr << "*** File Open Failed ***" << std::endl;
			delete file;
			file = Q_NULLPTR;
			return;
		}
		ui->stLabel->setText(QString("Open %1 Successfully!").arg(fileName));
	}
	if(file == Q_NULLPTR) // 文件未打开,不能进行后续操作
		return;

	if(gotBytes < fileBytes) // 文件未接收完
	{
		gotBytes += receive->bytesAvailable();
		ui->recvProg->setValue(gotBytes);
		file->write(receive->readAll());
	}
	if(gotBytes == fileBytes) // 文件接收完
	{
		receive->close(); // 关socket
		file->close(); // 关文件
		delete file;
		ui->stLabel->setText(QString("Finish receiving %1").arg(fileName));
		ui->listenBtn->setEnabled(true);
	}
}

/*--- 出错处理 ---*/
void RecevieFile::show_error(QAbstractSocket::SocketError)
{
	std::cerr << "*** Socket Error ***" << std::endl;
	qDebug() << receive->errorString();
	receive->close(); // 关cocket
	receive = Q_NULLPTR;
	file = Q_NULLPTR;
	fileName.clear(); // 清空文件名
	fileBytes = gotBytes = nameSize = 0;
	ui->recvProg->reset(); // 进度条归零
	ui->listenBtn->setEnabled(true); // 启用监听按钮
	ui->stLabel->setText(QString("*** SOCKET ERROR"));
}

完整工程

Qt实现TCP文件传输例子 ← \leftarrow 这是对应的整个工程,贼贵,还没多出什么实际有用的代码
→ \rightarrow 完整课设 ← \leftarrow 不如下这个,需要的积分更少

  • 16
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 32
    评论
### 回答1: 可以使用Qt中的QTcpSocket类和QTcpServer类来实现TCP文件传输。具体步骤如下: 1. 创建一个QTcpServer对象,并在其上调用listen()方法来监听来自客户端的连接请求。 2. 当有客户端连接请求到达时,QTcpServer会触发newConnection()信号。在该信号的槽函数中,可以使用QTcpServer的nextPendingConnection()方法获取一个QTcpSocket对象来和客户端进行通信。 3. 在QTcpSocket对象上调用connectToHost()方法连接服务器端,或者使用已连接的QTcpSocket对象进行数据传输。 4. 在QTcpSocket对象上调用write()方法发送文件内容,或者在接收方使用QTcpSocket的readyRead()信号和read()方法接收文件内容。 5. 传输完成后,关闭QTcpSocket对象和QTcpServer对象。 具体实现可以参考Qt官方文档和示例程序。 ### 回答2: Qt是一个开发C++桌面应用程序的跨平台框架,它提供了丰富的类库和工具来简化程序开发。要实现TCP文件传输,可以借助Qt的网络模块。 首先,我们需要创建一个TCP服务器和一个TCP客户端。服务器端需要监听指定的端口并接受客户端连接,而客户端需要连接到服务器并发送文件数据。 在服务器端,我们可以使用QTcpSocket和QTcpServer类来创建一个TCP服务器。可以使用QTcpServer的listen方法来绑定服务器IP地址和监听的端口。一旦有客户端连接到服务器,QTcpServer会发出newConnection信号,我们可以在这个信号槽函数中处理客户端连接。 在客户端,我们同样可以使用QTcpSocket类来创建一个TCP连接。通过调用QTcpSocket的connectToHost方法,我们可以连接到服务器的IP地址和端口。一旦连接建立成功,我们可以使用QTcpSocket的write方法来发送文件数据。 为了实现文件的传输,我们可以使用QFile类来打开文件并读取数据。在服务器端,可以将文件内容分割为较小的数据块,并使用QTcpSocket的write方法将这些数据块发送给客户端。在客户端,我们可以使用QTcpSocket的read方法接收服务器发送的数据块,并将其写入文件中。 在文件传输过程中,我们还可以使用QTcpSocket的readyRead和bytesWritten信号来处理读取和写入文件数据的细节,以确保数据的完整性和正确性。 通过以上步骤,我们可以使用Qt实现TCP文件传输。同时,Qt还提供了其他一些类和方法来处理网络连接的错误和异常,并提供了完善的错误处理机制来增加程序的稳定性和可靠性。 ### 回答3: 在Qt实现TCP文件传输需要使用QTcpSocket类和QFile类。首先,需要创建一个QTcpSocket的实例来处理网络连接。然后,连接到服务器并通过QTcpSocket的write方法发送文件的字节流。同时,使用QFile类来打开本地文件以便读取字节流并将其发送给服务器。 以下是一个简单的示例代码: ```cpp // 客户端: QTcpSocket socket; socket.connectToHost("服务器地址", 端口号); if (socket.waitForConnected()) { QFile file("本地文件路径"); if (file.open(QIODevice::ReadOnly)) { QByteArray data = file.readAll(); file.close(); socket.write(data); socket.waitForBytesWritten(); socket.disconnectFromHost(); } } else { // 连接失败的处理逻辑 } // 服务器: QTcpServer server; server.listen(QHostAddress::Any, 端口号); connect(&server, SIGNAL(newConnection()), this, SLOT(onNewConnection())); void YourClass::onNewConnection() { QTcpSocket *socket = server.nextPendingConnection(); connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); } void YourClass::onReadyRead() { QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); QByteArray data = socket->readAll(); // 处理接收到的文件数据 } ``` 上面的代码只是实现TCP文件传输的基本框架,你还可以根据自己的需求进行相关的逻辑和错误处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值