传输文件效果图如下:客户端先连接服务器,然后服务器点击 “选择文件”,选择文件之后点击 “发送文件”,客户端用一个进度条显示接收数据的进度。
服务器端代码:
widget.h:
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFile>
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
// 发送文件数据
void sendFileData();
private slots:
void on_btnSelect_clicked();
void on_btnSend_clicked();
private:
Ui::Widget *ui;
QTcpServer *tcpServer; // 监听套接字
QTcpSocket *tcpSocket; // 通信套接字
QFile file; // 全局文件对象
QString fileName; // 文件名称
int fileSize; // 文件大小
int sendSize; // 已经发送文件大小
QTimer timer; // 定时器
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QFileInfo>
#include <QFileDialog>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 实例化套接字对象
tcpServer = new QTcpServer(this);
tcpSocket = new QTcpSocket(this);
// 监听客户端连接
tcpServer->listen(QHostAddress::Any, 8888);
// 如果客户端成功和服务器连接会触发 newConnection 信号
connect(tcpServer, &QTcpServer::newConnection, [=](){
// 取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
// 获取客户端的 ip和端口
QString ip = tcpSocket->peerAddress().toString();
int port = tcpSocket->peerPort();
// 显示到界面上
QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
ui->textEdit->setText(str);
// 接收数据
connect(tcpSocket, &QTcpSocket::readyRead, [=](){
// 获取客户端返回的数据
QByteArray buf = tcpSocket->readAll();
if (QString(buf) == "file done")
{
ui->textEdit->append("文件发送成功");
file.close();
// 断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
});
});
// 启动计时器
connect(&timer, &QTimer::timeout, [=](){
// 停止定时器
timer.stop();
// 发送文件数据
sendFileData();
});
}
Widget::~Widget()
{
delete ui;
}
// 选择文件
void Widget::on_btnSelect_clicked()
{
// 获取文件名称
QString filePath = QFileDialog::getOpenFileName();
if (!filePath.isEmpty())
{
fileName.clear();
fileSize = 0;
sendSize = 0;
// 获取文件信息
QFileInfo info(filePath);
fileName = info.fileName(); // 获取文件名称
fileSize = info.size(); // 获取文件大小
// 实例化文件对象
file.setFileName(filePath);
// 以只读方式打开文件
if (file.open(QFile::ReadOnly))
{
// 提示打开文件的路径
ui->textEdit->append(filePath);
}
}
}
// 发送文件
void Widget::on_btnSend_clicked()
{
ui->textEdit->append("正在发送文件...");
// 先发送文件信息(文件名|文件大小)
QString fileInfo = QString("%1|%2").arg(fileName).arg(fileSize);
// 发送
int len = tcpSocket->write(fileInfo.toUtf8());
if (len > 0)
{
// 发送真正的文件数据;防止 TCP 粘包文件,需要通过定时器延迟 20ms;
timer.start(20);
}
else
{
qDebug() << "发送文件信息失败";
file.close();
}
}
// 发送文件数据
void Widget::sendFileData()
{
qint64 len = 0;
do
{
// 缓冲区大小,用来存储每次从文件中读取的数据
char buf[1024 * 4] = {0};
// 从文件中读取数据,存储到缓冲区中;返回实际读取的大小
len = file.read(buf, sizeof(buf));
// 将读取到的数据发送给客户端;返回实际发送的大小
len = tcpSocket->write(buf, len);
// 累加已经发送数据的大小
sendSize += len;
}while(len > 0);
}
客户端代码:
widget.h:
#include <QWidget>
#include <QTcpSocket>
#include <QFile>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnConnect_clicked();
private:
Ui::Widget *ui;
QTcpSocket *tcpSocket; // 通信套接字
QFile file; // 全局文件对象
QString fileName; // 文件名称
int fileSize; // 文件大小
int reveSize; // 实际接收文件大小
bool isFileInfo; // 标志位:判断当前接收的数据是否是文件信息
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QStringList>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
isFileInfo = true;
ui->progressBar->setValue(0); // 进度条初始值为 0
// 实例化通信套接字对象
tcpSocket = new QTcpSocket(this);
// 当接收到数据的时候触发 readyRead 信号
connect(tcpSocket, &QTcpSocket::readyRead, [=](){
// 取出获取到的数据
QByteArray buf = tcpSocket->readAll();
if (isFileInfo == true) // 接收到的是文件信息
{
isFileInfo = false;
reveSize = 0;
// 截取数据
QStringList list = QString(buf).split('|');
fileName = list.at(0); // 文件名称
fileSize = QString(list.at(1)).toInt(); // 文件大小
// 设置进度条
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(fileSize / 1024);
ui->progressBar->setValue(0);
// 实例化文件对象
file.setFileName(fileName);
// 以只写方式打开文件
if (!file.open(QFile::WriteOnly))
{
qDebug() << "打开文件失败";
}
}
else // 接收到的是文件数据
{
// 将接收到的数据写入文件
int len = file.write(buf);
// 累计接收接收文件大小
reveSize += len;
// 更新进度条的值
ui->progressBar->setValue(reveSize / 1024);
if (reveSize == fileSize)
{
// 文件接收成功之后,给服务器响应一下
tcpSocket->write("file done");
ui->textEdit->append("文件接收完成");
file.close();
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
});
}
Widget::~Widget()
{
delete ui;
}
// 连接服务器
void Widget::on_btnConnect_clicked()
{
// 获取服务器 ip 和端口
QString ip = ui->lineEdit_IP->text();
int port = ui->lineEdit_Port->text().toInt();
// 连接到服务器
tcpSocket->connectToHost(QHostAddress(ip), port);
}