Qt实现文件传输服务器
- 1、前言
- 2、服务器
-
- [2.1 服务器UI界面](#2.1 服务器UI界面)
- 2.2添加网络模块和头文件
- 2.3、创建服务器对象
- [2.4 连接有新连接的信号与槽](#2.4 连接有新连接的信号与槽)
- 2.5实现有新连接处理的槽函数
- [2.6 选择文件按钮实现](#2.6 选择文件按钮实现)
-
- [2.6.1 连接按钮点击的信号与槽](#2.6.1 连接按钮点击的信号与槽)
- [2.6.2 添加头文件](#2.6.2 添加头文件)
- [2.6.3 创建所需对象](#2.6.3 创建所需对象)
- [2.6.3 选择文件按钮实现](#2.6.3 选择文件按钮实现)
- [2.7 发送文件按钮实现](#2.7 发送文件按钮实现)
- 2.7.1添加定时器解决粘包问题
- [2.7.2 连接定时器结束信号与槽](#2.7.2 连接定时器结束信号与槽)
- 2.7.3定时器结束槽函数实现
- 3、头文件和.cpp文件
-
- [3.1 Widget.h文件](#3.1 Widget.h文件)
- [3.2 .cpp文件](#3.2 .cpp文件)
- 4、总结
1、前言
|-------------------------------------------------------------------------------------------------------|
| 记录自己对于Qt实现文件传输的学习过程,方便自己日后回顾,也可以给别人提供参考借鉴,目录就是实现的过程,大家可以按照目录逐步浏览,也可以点击目录跳转到所需部分,这个是服务器端,客户端在主页另外一篇博客。 |
2、服务器
2.1 服务器UI界面

2.2添加网络模块和头文件

|-------------------|
| 添加完网络模块构建一下,添加头文件 |
cpp
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>

2.3、创建服务器对象
cpp
#define PORT 8000 //端口号
QTcpServer *tcpServer; //创建服务器对象指针
QTcpSocket *tcpSocket; //通信套接字


|----------------|
| 创建服务器对象,监听连接对象 |
cpp
tcpServer = new QTcpServer(this); //创建服务器对象
tcpServer->listen(QHostAddress::Any,PORT); //监听任意ip地址和端口号

2.4 连接有新连接的信号与槽
|-------|
| 创建槽函数 |
cpp
private slots:
void newConnectionHandler(); //新连接处理槽函数
|---------------|
| 连接有新连接的信号和槽函数 |
cpp
connect(tcpServer,&QTcpServer::newConnection,this,&Widget::newConnectionHandler); //处理新连接
2.5实现有新连接处理的槽函数
|----------------------------|
| 鼠标放在函数上,ALT+Enter
,点击添加定义 |

|---------------|
| 未连接成功前设置按钮不可按 |
cpp
//设置按钮不可按
ui->selectPushButton->setEnabled(false); //选择文件按钮不可按
ui->sendPushButton->setEnabled(false); //发送文件按钮不可按

|----------|
| 新连接槽函数实现 |
cpp
//处理新连接
void Widget::newConnectionHandler()
{
//建立Tcp连接
tcpSocket = tcpServer->nextPendingConnection(); //取出下一个连接的套接字
//获取ip地址和端口号
QString ip = tcpSocket->peerAddress().toString(); //ip地址
qint16 Port = tcpSocket->peerPort(); //端口号
//显示ip地址和端口号
ui->ipLineEdit->setText(ip); //在ip输入框上显示
ui->portLineEdit->setText(QString::number(Port)); //在Port输入框中显示
QString connectTemp = QString("[%1:%2] 已连接").arg(ip).arg(Port); //已连接文本
ui->messageTextEdit->setText(connectTemp); //显示已连接信息
//设置文件按钮为可按
ui->selectPushButton->setEnabled(true); //设置选择文件按钮位可按
ui->sendPushButton->setEnabled(true); //设置发送文件按钮位可按
}
2.6 选择文件按钮实现
2.6.1 连接按钮点击的信号与槽
|----------------------------|
| 把鼠标放到选择文件的按钮控件上,右键,点击转到槽
|

|---------------|
| 点击clicked()
|

2.6.2 添加头文件

|---------------------------------------|
| 添加QMessageBox是为了弹窗消息框,添加QDebug是为了打印信息 |
2.6.3 创建所需对象
cpp
QFile file; //创建文件对象
QString fileName; //文件名字
qint64 fileSize; //文件大小
qint64 senddFileSize; //发送文件大小

2.6.3 选择文件按钮实现
cpp
//选择文件按钮
void Widget::on_selectPushButton_clicked()
{
QString filePath = QFileDialog::getOpenFileName(this,"open","../"); //准备打开文件的路径
//打开的文件路径不为空
if(!filePath.isEmpty())
{
//获取文件信息
QFileInfo fileInfo(filePath); //文件信息
fileName = fileInfo.fileName(); //文件名字
fileSize = fileInfo.size(); //文件大小
senddFileSize = 0; //发送文件大小为0
//以只读打开文件
file.setFileName(filePath); //准备打开的文件
bool fileisOk = file.open(QIODevice::ReadOnly); //以只读形式打开
//打开文件失败
if(fileisOk==false)
{
QMessageBox::warning(this,"打开文件","打开文件失败"); //显示打开文件失败消息框
}
//文件路径显示
ui->messageTextEdit->setText(filePath); //显示文件路径
ui->selectPushButton->setEnabled(false); //打开文件成功后选择文件按钮失效
ui->sendPushButton->setEnabled(true); //发送按钮文件生效
}
else
{
QMessageBox::warning(this,"打开文件失败","文件不能为空"); //显示打开文件错误消息框
}
}
2.7 发送文件按钮实现
|---------------------------|
| 跟选择文件一样,转到槽,点击clicked()
|
2.7.1添加定时器解决粘包问题
|-------|
| 添加头文件 |

|---------|
| 创建定时器对象 |
cpp
QTimer sendFileTimer; //发送文件定时器
2.7.2 连接定时器结束信号与槽
|--------------|
| 创建定时器结束处理槽函数 |
cpp
void sendFileTimeoutHandler(); //发送文件结束定时器处理
cpp
sendFileTimer = new QTimer(this); //创建定时器对象
|-------------|
| 连接定时器结束信号与槽 |
cpp
connect(sendFileTimer,&QTimer::timeout,this,&Widget::sendFileTimeoutHandler); //定时器结束处理
2.7.3定时器结束槽函数实现
cpp
//定时器结束处理
void Widget::sendFileTimeoutHandler()
{
sendFileTimer->stop(); //停止定时器
char buf[4*1024] = {0}; //缓冲区
qint64 bytesRead = file.read(buf, sizeof(buf)); // 读取实际字节数
//成功读取到数据
if(bytesRead>0)
{
qint64 bytesWritten = tcpSocket->write(buf, bytesRead); // 发送实际读取的字节
//发送错误
if (bytesWritten == -1) {
QMessageBox::warning(this, "错误", "发送失败");
return;
}
senddFileSize += bytesWritten; //发送的文件大小
// 未发送完则继续触发定时器
if (senddFileSize < fileSize) {
sendFileTimer->start(20); //20ms触发一次定时器
}
else {
QMessageBox::information(this, "完成", "文件发送完毕");
file.close();
}
}
else if(senddFileSize < fileSize)
{
QMessageBox::warning(this, "错误", "文件读取失败");
}
}
3、头文件和.cpp文件
3.1 Widget.h文件
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QFileDialog>
#include <QFileInfo>
#include <QFile>
#include <QMessageBox>
#include <QDebug>
#include <QTimer>
#define PORT 8000 //端口号
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void newConnectionHandler(); //新连接处理槽函数
void sendFileTimeoutHandler(); //发送文件结束定时器处理
void on_selectPushButton_clicked(); //选择文件按钮实现
void on_sendPushButton_clicked(); //发送文件按钮实现
private:
Ui::Widget *ui;
QTcpServer *tcpServer; //创建服务器对象指针
QTcpSocket *tcpSocket; //通信套接字
QFile file; //创建文件对象
QString fileName; //文件名字
qint64 fileSize; //文件大小
qint64 senddFileSize; //发送文件大小
QTimer *sendFileTimer; //发送文件定时器
};
#endif // WIDGET_H
3.2 .cpp文件
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// this->setWindowFlag(Qt::FramelessWindowHint); //去边框
//设置按钮不可按
ui->selectPushButton->setEnabled(false); //选择文件按钮不可按
ui->sendPushButton->setEnabled(false); //发送文件按钮不可按
tcpServer = new QTcpServer(this); //创建服务器对象
tcpServer->listen(QHostAddress::Any,PORT); //监听任意ip地址和端口号
sendFileTimer = new QTimer(this); //创建定时器对象
connect(tcpServer,&QTcpServer::newConnection,this,&Widget::newConnectionHandler); //处理新连接
connect(sendFileTimer,&QTimer::timeout,this,&Widget::sendFileTimeoutHandler); //定时器结束处理
}
Widget::~Widget()
{
delete ui;
}
//处理新连接
void Widget::newConnectionHandler()
{
//建立Tcp连接
tcpSocket = tcpServer->nextPendingConnection(); //取出下一个连接的套接字
//获取ip地址和端口号
QString ip = tcpSocket->peerAddress().toString(); //ip地址
qint16 Port = tcpSocket->peerPort(); //端口号
//显示ip地址和端口号
ui->ipLineEdit->setText(ip); //在ip输入框上显示
ui->portLineEdit->setText(QString::number(Port)); //在Port输入框中显示
QString connectTemp = QString("[%1:%2] 已连接").arg(ip).arg(Port); //已连接文本
ui->messageTextEdit->setText(connectTemp); //显示已连接信息
//设置文件按钮为可按
ui->selectPushButton->setEnabled(true); //设置选择文件按钮位可按
ui->sendPushButton->setEnabled(true); //设置发送文件按钮位可按
}
//定时器结束处理
void Widget::sendFileTimeoutHandler()
{
sendFileTimer->stop(); //停止定时器
char buf[4*1024] = {0}; //缓冲区
qint64 bytesRead = file.read(buf, sizeof(buf)); // 读取实际字节数
//成功读取到数据
if(bytesRead>0)
{
qint64 bytesWritten = tcpSocket->write(buf, bytesRead); // 发送实际读取的字节
//发送错误
if (bytesWritten == -1) {
QMessageBox::warning(this, "错误", "发送失败");
return;
}
senddFileSize += bytesWritten; //发送的文件大小
// 未发送完则继续触发定时器
if (senddFileSize < fileSize) {
sendFileTimer->start(20); //20ms触发一次定时器
}
else {
QMessageBox::information(this, "完成", "文件发送完毕");
file.close();
}
}
else if(senddFileSize < fileSize)
{
QMessageBox::warning(this, "错误", "文件读取失败");
}
}
//选择文件按钮
void Widget::on_selectPushButton_clicked()
{
QString filePath = QFileDialog::getOpenFileName(this,"open","../"); //准备打开文件的路径
//打开的文件路径不为空
if(!filePath.isEmpty())
{
//获取文件信息
QFileInfo fileInfo(filePath); //文件信息
fileName = fileInfo.fileName(); //文件名字
fileSize = fileInfo.size(); //文件大小
senddFileSize = 0; //发送文件大小为0
//以只读打开文件
file.setFileName(filePath); //准备打开的文件
bool fileisOk = file.open(QIODevice::ReadOnly); //以只读形式打开
//打开文件失败
if(fileisOk==false)
{
QMessageBox::warning(this,"打开文件","打开文件失败"); //显示打开文件失败消息框
}
//文件路径显示
ui->messageTextEdit->setText(filePath); //显示文件路径
ui->selectPushButton->setEnabled(false); //打开文件成功后选择文件按钮失效
ui->sendPushButton->setEnabled(true); //发送按钮文件生效
}
else
{
QMessageBox::warning(this,"打开文件失败","文件不能为空"); //显示打开文件错误消息框
}
}
//发送文件按钮
void Widget::on_sendPushButton_clicked()
{
//发送文件信息
QString fileMes = QString("%1##%2\n").arg(fileName).arg(fileSize); //发送文件的名字和大小
qint64 fileLength = tcpSocket->write(fileMes.toUtf8().data()); //发送文件长度
//发送文件成功
if(fileLength > 0)
{
//发送文件的真正内容
//距离太久会产生粘包问题
//使用定时器解决粘包问题
sendFileTimer->start(20); //间隔20ms触发timeout定时器结束信号
}
else
{
QMessageBox::warning(this,"发送文件","发送文件失败"); //显示发送文件失败消息框
file.close(); //关闭文件
tcpSocket->disconnectFromHost(); //断开连接
tcpSocket->close(); //关闭连接
}
}
4、总结
|-------------------------------------------------------------------------------------------------------------|
| 以上就是Qt实现文件传输服务器端的整个过程了,浏览过程中,如若发现错误,欢迎大 家指正,有问题的可以评论区留言或者私信。最后,如果大家觉得有所帮助,可以点一 下赞,谢谢大家!未来是未知的,愿大家保持冷静,继续前行! |