文件发送与文件接收的设计思路(基于原有客户端和服务器)
Qt 实现 TCP 文件传输(客户端 + 服务器端)
本文详细梳理 Qt 基于 TCP 协议实现文件传输的核心流程,包含客户端文件发送和服务器端文件接收的完整逻辑,重点区分消息头解析(结构化数据)和文件内容传输(字节流数据)的不同处理方式。
一、客户端:文件发送流程
1. 前置准备:头文件与核心参数
1.1 所需头文件
#include//创建文件对象指针用
#include//打开文件用
1.2 核心参数定义
//传输文件相关参数
QFile *m_LocalFiles; // 要传输的文件
qint64 m_TotalBytes; // 数据总大小
qint64 m_BytesWrites; // 已经发送数据大小
qint64 m_BytesToWrites; // 剩下数据大小
qint64 m_LoadSizes; // 每次发送数据的大小
QString m_FileNames; // 保存文件信息
QByteArray m_OutDataBlock; //数据缓冲区(即存放每次要发送的数据)
2. 发送流程拆解
2.1 步骤 1:选择文件并检查可用性
通过QFileDialog获取待传输文件路径,绑定到文件对象指针,并检查文件是否可读取:
m_FileNames=QFileDialog::getOpenFileName(this, "请选择要传输的文件");
// 前置条件:检查文件是否能打开
if(!m_LocalFiles->open(QFile::ReadOnly))
2.2 步骤 2:编写文件头信息
文件头需包含固定结构化数据,用于服务器端解析:文件总大小 + 文件名大小(字节数) + 文件名
m_TotalBytes=m_LocalFiles->size();
// 文件名大小:strCurrFileName的字节数
// 文件名:截取路径中的文件名(注意分隔符使用/,避免服务器解析异常)
QString strCurrFileName=m_FileNames.right(m_FileNames.size()m_FileNames.lastIndexOf("/")-1);
2.3 步骤 3:设置缓冲区参数
定义每次发送的数据块大小(固定 4KB):
m_LoadSizes=4*1024;
2.4 步骤 4:发送文件(头部 + 内容)
-
第一步:绑定数据流并写入头部信息
// 缓冲区绑定数据流 QDataStream sendout(&m_OutDataBlock,QIODevice::WriteOnly); // 写入头部信息:总大小 + 文件名长度 + 文件名 sendout< -
第二步:发送头部信息
client_Socket_File->write(m_OutDataBlock); -
第三步:循环发送文件内容缓冲区每次读取固定大小数据发送,剩余数据不足 4KB 时直接发送:
//缓冲区每次取出固定大小的数据进行发送,如果小于4kb就直接发送 m_OutDataBlock=m_LocalFiles->read(qMin(m_BytesToWrites,m_LoadSizes)); m_BytesToWrites= m_BytesToWrites-(int)client_Socket_File->write(m_OutDataBlock);
2.5 发送控制条件
- 发送开始条件:
m_BytesToWrites>0 - 发送结束条件:
m_TotalBytes==m_BytesWrites
二、服务器端:文件接收流程
1. 核心参数定义
// 文件传输相关成员变量
qint64 m_FileTotalBytes; // 文件总字节数
qint64 m_FileBytesReceived; // 已接收文件字节数
qint64 m_FileNameSizes; // 文件名称长度(字节)
QString m_FileNames; // 接收的文件名
QFile *m_LocalFiles; // 本地文件对象指针(用于保存接收文件)
QByteArray m_InBlocks; // 接收数据缓冲区(存储文件数据块)
2. 接收流程拆解
2.1 阶段一:接收并解析头部信息
通过QDataStream解析结构化的消息头(精确读取文件总大小、文件名长度、文件名):
// 创建数据流接收socket的数据
QDataStream in=QDataStream(P_TCPFileScoketObject);
// 接收头部信息:文件总大小 + 文件名大小
in>>m_FileTotalBytes>>m_FileNameSizes;
// 接收文件名(>> 从socket读取对应字节数存入m_FileNames)
in>>m_FileNames;
// 创建本地文件(保存到程序运行目录)
m_LocalFiles=new QFile(QApplication::applicationDirPath()+"//"+m_FileNames);
// 检查是否能够打开
m_LocalFiles=new QFile(QApplication::applicationDirPath()+"//"+m_FileNames);
2.2 阶段二:接收文件内容
文件内容为纯字节流,直接从 Socket 读取并写入本地文件:
// 读取数据并写入缓冲区
m_InBlocks=P_TCPFileScoketObject->readAll();
// 从缓冲区读取数据写入本地文件
m_LocalFiles->write(m_InBlocks);
2.3 阶段三:结束接收判断
if(m_FileBytesReceived==m_FileTotalBytes)//文件接收结束判断条件
三、关键注意事项
-
QDataStream 的适用场景:适用于处理结构化数据(如文件头中的文件大小、文件名大小),因为消息头需要精确解析固定长度的字段,保证解析准确性。
-
Socket 直接读取的适用场景:适合读取无结构的纯字节流(如文件内容),无需解析固定格式,可最大化读取效率。
-
分隔符注意:截取文件名时使用
/作为路径分隔符,避免使用//等特殊符号,防止服务器端解析文件名失败








