在接收数据时,首先需要确定协议头和协议尾,以保证接收到的数据是完整的。根据代码中发送数据的格式,可以得知协议头为 0xCD,协议尾为 0xDC。
在 readyRead 信号的槽函数中,可以通过调用 QTcpSocket 的 readAll() 函数来获取所有可读取的数据,并将其保存到 QByteArray 类型的变量中。
接下来需要根据发送数据格式解析接收到的数据。具体做法是:先从 QByteArray 中提取出协议头、文件名长度、文件名、块数和每块大小等信息;然后按照块数循环读取每一块内容,并将其拼接起来保存到本地文件。
以下是一个可能实现方式(仅供参考):
// 定义状态机
enum State {
WAIT_HEADER, // 等待协议头
READ_FILENAME_LEN, // 读取文件名长度
READ_FILENAME, // 读取文件名
READ_BLOCK_COUNT, // 读取块数
READ_BLOCK_SIZE, // 读取每块大小
READ_CONTENT_SIZE, // 读取内容大小
READ_CONTENT // 读取内容
};
QByteArray receivedData; // 存储已经接收到但未处理完整的数据包
quint16 fileNameLen = 0; // 文件名长度
QString fileName; // 文件名(UTF-8编码)
quint64 blockCount = 0; // 块数
quint64 blockSize = 0; // 每块大小
quint64 receivedBlockCount = 0; // 已经接收到的块数
quint64 currentBlockSize = 0; // 当前块的大小
QFile file("your_file_path", this); // 待保存文件
State state = WAIT_HEADER;
connect(socket, &QTcpSocket::readyRead, [=]() {
QByteArray buffer = socket->readAll();
receivedData.append(buffer);
while (!receivedData.isEmpty()) {
switch (state) {
case WAIT_HEADER:
if (receivedData[0] == char(0xCD)) {
receivedData.remove(0, 1);
state = READ_FILENAME_LEN;
} else {
qDebug() << "Invalid protocol header";
return;
}
break;
case READ_FILENAME_LEN:
if (receivedData.length() < sizeof(quint16)) {
return;
}
QDataStream nameLenStream(receivedData);
nameLenStream >> fileNameLen;
receivedData.remove(0, sizeof(quint16));
state = READ_FILENAME;
break;
case READ_FILENAME:
if (receivedData.length() < fileNameLen) {
return;
}
fileName = QString::fromUtf8(receivedData.left(fileNameLen));
receivedData.remove(0, fileNameLen);
state = READ_BLOCK_COUNT;
break;
case READ_BLOCK_COUNT:
if (receivedData.length() < sizeof(quint64)) {
return;
}
QDataStream blockCountStream(receivedData);
blockCountStream >> blockCount;
receivedBlockCount= 0;// 初始化已经接收到的块数为0
file.setFileName(fileName);// 设置要保存的文件名
if(!file.open(QIODevice::WriteOnly)){
qDebug()<<"Failed to open file for writing";
return;
}
receivedData.remove(0, sizeof(quint64));
state = READ_BLOCK_SIZE;
break;
case READ_BLOCK_SIZE:
if (receivedData.length() < sizeof(quint64)) {
return;
}
QDataStream blockSizeStream(receivedData);
blockSizeStream >> blockSize;
receivedData.remove(0, sizeof(quint64));
state = READ_CONTENT_SIZE;
break;
case READ_CONTENT_SIZE:
if (receivedData.length() < sizeof(quint64)) {
return;
}
QDataStream contentBlockSizeStream(receivedData);
contentBlockSizeStream >> currentBlockSize;//读取当前块的大小
receivedBlockCount++;//已接收到的块数加1
receivedData.remove(0, sizeof(quint64));
state = READ_CONTENT;
break;
case READ_CONTENT:
if (receivedData.length() < currentBlockSize) {
return;
}
QByteArray contentBlock = receivedData.left(currentBlockSize);//提取出当前块的内容
file.write(contentBlock);//将当前块的内容写入文件
receivedData.remove(0, currentBlockSize);
// 如果所有块都已经接收完毕,就关闭文件并返回
if (receivedBlockCount >= blockCount) {
file.close();
qDebug() << "File saved successfully";
return;
} else {
state = READ_CONTENT_SIZE; // 继续读取下一块的大小
}
break;
default:
qDebug() << "Unknown state";
}
}
});