服务端
serverwidget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include <QTcpServer> // 监听套接字
#include <QTcpSocket> // 通信套接字
#include <QFile>
#include <QTimer>
namespace Ui {
class ServerWidget;
}
class ServerWidget : public QWidget
{
Q_OBJECT
public:
explicit ServerWidget(QWidget *parent = 0);
~ServerWidget();
// 发送文件数据的函数
void sendData();
private slots:
void on_buttonSeleFile_clicked();
void on_buttonSendFile_clicked();
private:
Ui::ServerWidget *ui;
QTcpServer *tcpServer; // 监听套接字
QTcpSocket *tcpSocket; // 通信套接字
QFile file; // 文件对象
QString fileName; // 文件名字
qint64 fileSize; // 文件大小
qint64 sendSize; // 已发送文件大小
// 定时器
QTimer timer;
};
#endif // SERVERWIDGET_H
serverwidget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QFileDialog>
#include <QDebug>
#include <QFileInfo>
ServerWidget::ServerWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ServerWidget)
{
ui->setupUi(this);
this->setWindowTitle("服务器端口为9999");
// 未连接时,两个按钮都不能点击
ui->buttonSeleFile->setEnabled(false);
ui->buttonSendFile->setEnabled(false);
// 监听套接字
tcpServer = new QTcpServer(this);
// 监听ip和端口
tcpServer->listen(QHostAddress::Any, 9999);
// 如果客户端成功和服务器连接
// tcpServer会自动触发newConnection()
connect(tcpServer, &QTcpServer::newConnection, [=](){
// 取出建立好连接的套接字
tcpSocket = tcpServer->nextPendingConnection();
// 获取对方的ip和端口
QString ip = tcpSocket->peerAddress().toString();
qint16 port = tcpSocket->peerPort();
// 组包
QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
ui->textEdit->setText(str); // 显示到编辑区
qDebug() << str;
// 成功连接后,才能选择文件
ui->buttonSeleFile->setEnabled(true);
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();
// 发送文件
sendData();
});
}
ServerWidget::~ServerWidget()
{
delete ui;
}
// 选择文件按钮
void ServerWidget::on_buttonSeleFile_clicked()
{
// 文件路径
QString filePath = QFileDialog::getOpenFileName(this, "open", "../");
// 如果选择文件路径有效
if(filePath.isEmpty() == false)
{
// 先清空前面的内容
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(isOk == false)
{
qDebug() << "只读方式打开文件失败 85";
}
// 提示打开文件的路径
ui->textEdit->append(filePath);
ui->buttonSeleFile->setEnabled(false);
ui->buttonSendFile->setEnabled(true);
}else {
qDebug() << "选择文件路径出错 95";
}
}
// 发送文件按钮
void ServerWidget::on_buttonSendFile_clicked()
{
ui->buttonSendFile->setEnabled(false);
// 先发送文件头信息 文件名##文件大小
QString head = QString("%1##%2").arg(fileName).arg(fileSize);
// 发送头部信息
qint64 len = tcpSocket->write(head.toUtf8());
if(len > 0) // 头部信息发送成功
{
// 发送正在文件信息
// 防止TCP黏包问题,需要通过定时器延时
timer.start(200);
qDebug() << "头部信息发送成功";
}else {
qDebug() << "头部信息发送失败 114";
file.close();
ui->buttonSeleFile->setEnabled(true);
ui->buttonSendFile->setEnabled(false);
}
}
// 发送数据方法
void ServerWidget::sendData()
{
ui->textEdit->append("正在发送文件......");
qint64 len = 0;
do{
// 每次发送数据的大小
char buf[4*1024] = {0};
len = 0;
// 往文件中读数据
len = file.read(buf, sizeof(buf));
// 发送数据, 读多少发多少
len = tcpSocket->write(buf, len);
// 发送的数据累加
sendSize += len;
qDebug() << "sendSize=" << sendSize << " fileSize=" << fileSize;
}while(len > 0);
// 是否发送文件完毕
if(sendSize == fileSize)
{
ui->textEdit->append("文件发送完毕");
file.close();
// 把客户端关了
// tcpSocket->disconnectFromHost();
// tcpSocket->close();
}
}
客户端
clientwidget.h
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QFile>
namespace Ui {
class ClientWidget;
}
class ClientWidget : public QWidget
{
Q_OBJECT
public:
explicit ClientWidget(QWidget *parent = 0);
~ClientWidget();
private slots:
void on_buttonConnect_clicked();
private:
Ui::ClientWidget *ui;
QTcpSocket *tcpSocket;
QFile file; // 文件对象
QString fileName; // 文件名字
qint64 fileSize; // 文件大小
qint64 recvSize; // 已接收文件大小
bool isStart;
};
#endif // CLIENTWIDGET_H
clientwidget.cpp
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QDebug>
#include <QMessageBox>
#include <QHostAddress>
ClientWidget::ClientWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::ClientWidget)
{
ui->setupUi(this);
// 设置进度条当前值为0
ui->progressBar->setValue(0);
this->setWindowTitle("客户端");
//
tcpSocket = new QTcpSocket(this);
// 与服务器建立连接
connect(tcpSocket, &QTcpSocket::connected, [=](){
// 提示连接成功
ui->textEdit->clear();
ui->textEdit->append("和服务器连接成功,等待服务器传送文件……");
});
isStart = true;
connect(tcpSocket, &QTcpSocket::readyRead, [=](){
// 取出接收的内容
QByteArray buf = tcpSocket->readAll();
if(isStart == true)
{// 接收头
isStart = false;
// 解析头部信息 QString buf = "hello##1024"
// 初始化
// 将字节数组buf转为string并拆包
fileName = QString(buf).section("##", 0, 0);
fileSize = QString(buf).section("##", 1, 1).toInt();
recvSize = 0;
// 打开文件
file.setFileName(fileName);
// 只写方式方式,打开文件
bool isOk = file.open(QIODevice::WriteOnly);
qDebug() << "isOk" << isOk;
if(isOk == false)
{
qDebug() << "WriteOnly error 35";
tcpSocket->disconnectFromHost(); //断开连接
tcpSocket->close(); //关闭套接字
return; // 如果打开文件失败,中断函数
}
// 弹出对话框, 显示接收文件的信息
QString str = QString("接收的文件:[%1: %2kb]").arg(fileName).arg(fileSize/1024);
ui->textEdit->append(str);
ui->textEdit->append("正在接收文件……");
// 设置进度条
ui->progressBar->setMinimum(0); // 最小值
ui->progressBar->setMaximum(fileSize/1024);
ui->progressBar->setValue(0); // 当前值
}else {// 文件信息
qint64 len = file.write(buf);
if(len > 0)
{
recvSize += len; //累计接收大小
qDebug() << len;
}
// 更新进度条
ui->progressBar->setValue(recvSize/1024);
qDebug() << "1111";
// recvSize += len;
if(recvSize == fileSize) // 文件接收完毕
{
// 先给服务器发送(接收文件完成的信息)
tcpSocket->write("file done");
file.close(); // 关闭文件
ui->textEdit->append("文件接收完成");
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
}
});
}
ClientWidget::~ClientWidget()
{
delete ui;
}
void ClientWidget::on_buttonConnect_clicked()
{
// 获取服务器的ip和端口
QString ip = ui->lineEditIP->text();
quint16 port = ui->lineEditPort->text().toInt();
// 连接服务器
tcpSocket->connectToHost(QHostAddress(ip), port);
isStart = true;
//设置进度条
ui->progressBar->setValue(0);
}