文章目录
一、文件传输服务器端
- 服务器端前端设置
- pro文件中添加配置 QT += network CONFIG+= C++11
- 头文件中定义变量
serverwidget.hQTcpServer *tcpServer; // 监听套接字 QTcpSocket *tcpSocket; // 通信套接字 QFile file; // 传输的文件对象 QString fileName; // 文件名字 qint64 fileSize; // 文件大小 qint64 sendSize; // 已经发送的文件的大小 QTimer timer; // 定时器 void sendData(); // 发送文件数据
- 文件传输方法的实现
先设置TCPServer和TCPSocket通信的连接
添加文件选择对话框,读取所选文件的信息(文件名以及大小)
文件传输,先传头部信息,为了防止粘包使用定时器在头信息传输成功后再传输文件内容ServerWidget::ServerWidget(QWidget *parent) : QWidget(parent), ui(new Ui::ServerWidget) { ui->setupUi(this); // 初始化时候两个按钮应该是无效的 ui->buttonSend->setEnabled(false); ui->buttonFile->setEnabled(false); // 监听套接字 tcpServer = new QTcpServer(this); // 监听 tcpServer->listen(QHostAddress::Any, 8888); setWindowTitle("服务端口为:8888"); // 如果客户端和服务器连接成功,tcpServer会自动触发 newConnection() connect(tcpServer, &QTcpServer::newConnection, [=](){ // 获取连接好的套接字 tcpSocket = tcpServer->nextPendingConnection(); // 获取对方的ip好端口 QString ip = tcpSocket->peerAddress().toString(); quint16 port = tcpSocket->peerPort(); QString str = QString("[%1, %2] 成功连接").arg(ip).arg(port); ui->textEdit->append(str); // 连接成功后启用文件选择按钮 ui->buttonFile->setEnabled(true); }); connect(&timer, &QTimer::timeout, [=](){ // 关闭定时器 timer.stop(); sendData(); }); } ServerWidget::~ServerWidget() { delete ui; } // 选择文件的按钮 void ServerWidget::on_buttonFile_clicked() { QString filePath = QFileDialog::getOpenFileName(this, "open", "../"); if(false == filePath.isEmpty()){ // 如果选择的文件有效 // 获取信息前应当先清空 fileName.clear(); fileSize = 0; // 获取文件信息(以只读的方式打开) QFileInfo info(filePath); fileName = info.fileName(); fileSize = info.size(); sendSize = 0; // 发送文件的大小 // 以只读的方式打开文件 file.setFileName(filePath); // 指定文件名字 bool isOk = file.open(QIODevice::ReadOnly); // 只读方式打开文件 if(false == isOk){ ui->textEdit->append("只读方式打开文件失败 70"); } else{ ui->textEdit->append(QString("打开的文件是: %1").arg(filePath)); ui->buttonSend->setEnabled(true); } }else{ qDebug() << "选择文件路径出错 74"; } } // 发送文件按钮 void ServerWidget::on_buttonSend_clicked() { // 先发送文件头信息 文件名 ## 文件大小 QString head = QString("%1##%2").arg(fileName).arg(fileSize); qint64 len = tcpSocket->write(head.toUtf8()); // 返回发送了多少信息 if(len > 0){ // 头部信息发送成功 // 发送真正的文件信息 // 防止TCP粘包问题, 需要通过定时器延时20ms timer.start(20); }else{ ui->textEdit->append("头部信息发送失败"); file.close(); ui->buttonSend->setEnabled(false); } } void ServerWidget::sendData() { qint64 len = 0; do{ // 每次发送4k的数据 char buf[4*1024] = {0}; len = 0; // 读取file中数据,往buf中写数据,返回写入的数据长度 len = file.read(buf, sizeof(buf)); tcpSocket->write(buf, len); // 发送数据要累加 sendSize += len; }while(len > 0); // 判断是否发送文件完毕 if(sendSize == fileSize){ ui->textEdit->append("文件发送完毕\n\n\n"); file.close(); // 与客户端连接关闭 tcpSocket->disconnectFromHost(); tcpSocket->close(); } }
二、文件传输客户端
-
前端设计
-
clientwidget.cpp
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QTcpSocket>
#include <QMessageBox>
#include <QHostAddress>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
isHead = true;
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::readyRead,
[=](){
// 取出接收的内容
QByteArray buf = tcpSocket->readAll();
if(true == isHead){ // 头信息
isHead = false;
// 解析头部信息 buf = "文件名 ## 大小"
fileName = QString(buf).section("##", 0, 0); // 分割字符串
fileSize = QString(buf).section("##", 1, 1).toInt();
receiveSize = 0;
ui->textEdit->append(buf);
// 打开文件
file.setFileName(fileName);
bool isOK = file.open(QIODevice::WriteOnly);
if(false == isOK){
ui->textEdit->append("文件写入失败");
tcpSocket->disconnectFromHost();
tcpSocket->close();
return ;
}
// 设置进度条
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(fileSize/1024);
ui->progressBar->setValue(0);
}else{ // 文件信息
qint64 len = file.write(buf);
if(len > 0){
receiveSize += len;
ui->progressBar->setValue(receiveSize/1024);
}
if(receiveSize == fileSize){
file.close();
ui->textEdit->append("文件传输完成");
QMessageBox::information(this, "完成", "文件接收完成");
tcpSocket->disconnectFromHost();
tcpSocket->close();
isHead = true;
}
}
});
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_pushButton_clicked()
{
// 获取服务器的ip和端口
QString ip = ui->ipText->text();
qint16 port = ui->portText->text().toInt();
tcpSocket->connectToHost(QHostAddress(ip), port);
}