Demo:下载地址: https://download.csdn.net/download/guorong520/12408371
没有csdn积分的小伙伴可以留言你的QQ邮箱,我会发送Demo,相互交流学习
一、程序运行图
客户端
服务端
二、功能描述
1.开启服务器,客户端进行连接。
2.客户端向服务器上传文件,也可以下载服务器文件到客户端本地。
三、源码如下
1.客户端
Client.cpp
#include "Client.h"
#include "ui_Client.h"
Client::Client(QWidget *parent) :
QDialog(parent),
ui(new Ui::Client)
{
ui->setupUi(this);
initClient();
//1.上传
connect(this, &Client::changeUpText, ui->labelUpTip, &QLabel::setText);
connect(ui->btnConnect,&QPushButton::clicked,this,&Client::onConnect);
connect(m_client,&QTcpSocket::connected,this,&Client::onAcceptState);
connect(ui->btnUpload,SIGNAL(clicked(bool)),this,SLOT(onOpenFile()));
connect(ui->btnSend,&QPushButton::clicked,this,&Client::onSendFile);
//第一:此信号,不能写入局部函数中,其他函数可以执行此信号,但是参数仍保留上一次的值。
//第二:触发此信号,不会立即执行槽函数,执行完这个局部函数,才会执行槽函数。
//第三:信号槽不能重复定义
connect(m_client, SIGNAL(bytesWritten(qint64)),this, SLOT(updateProgress(qint64)));
//2.下载
connect(this, &Client::changePuText, ui->labelPuTip, &QLabel::setText);
connect(ui->btnRemote,&QPushButton::clicked,this,&Client::onResquestFileList);
connect(ui->listWidget,&QListWidget::itemSelectionChanged,this,&Client::onGetDownFileName);
connect(ui->btnDownload,&QPushButton::clicked,this,&Client::onDownRequest);
connect(m_client, &QTcpSocket::readyRead, this, &Client::onReceiveProgress);
}
Client::~Client()
{
delete ui;
}
void Client::initClient()
{
setWindowTitle("文件客户端");
ui->labelUpTip->setText(QString("请先连接:"));
m_client = new QTcpSocket(this);
m_totalBytes = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
m_cmd = 0;
m_payloadSize = 64 * 1024;
ui->progressBar->reset();
}
void Client::onConnect()
{
if(ui->editHost->text().isEmpty() || ui->editPort->text().isEmpty())
{
QMessageBox::information(this,"提示"," 主机和端口不能为空");
return;
}
emit changeUpText(QString("连接中"));
m_client->connectToHost(ui->editHost->text(),ui->editPort->text().toInt());
if(!m_client->waitForConnected(30000))
{
emit changeUpText(QString("连接失败!"));
}
}
void Client::onAcceptState()
{
emit changeUpText(QString("连接成功!"));
}
void Client::onOpenFile()
{
emit changeUpText(QString("等待打开文件:"));
m_fileName = QFileDialog::getOpenFileName(this);
if(!m_fileName.isEmpty())
{
emit changeUpText(QString("文件 %1 打开成功!").arg(m_fileName));
}
}
void Client::onSendFile()
{
//不是消息,并且文件名为空
if(m_cmd == 0 && m_fileName.isEmpty())
{
ui->labelUpTip->setText("请选择上传文件");
return;
}
m_localFile = new QFile(m_fileName);
if(!m_localFile->open(QFile::ReadOnly))
{
qDebug()<<"文件打开错误";
}
//计算文件大小
m_totalBytes = m_localFile->size();
//将发送缓冲区封装在QDataStream类型的变量中,方便传输数据
QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_5_7);
//构造临时文件头(总文件大小+文件名大小+消息类型+文件名)
sendOut<<qint64(0)<<qint64(0)<<qint64(0)<<clearFilePath(m_fileName);;
//计算文件总大小(总文件大小 = 文件大小 + 文件名 + sizeof(qint64)*3))
m_totalBytes += m_outBlock.size();
//将读写操作指向从头开始
sendOut.device()->seek(0);
//总大小和文件名大小和命令(必须为八个字节格式,里面存放值)
sendOut<<m_totalBytes<<qint64((m_outBlock.size()-sizeof(qint64)*3))<<qint64(UPLOAD_FILE);
m_cmd = UPLOAD_FILE;
qDebug()<<"发送总大小"<<m_totalBytes<<"文件名大小"<<qint64((m_outBlock.size()-sizeof(qint64)*3))
<<"文件名称"<<m_fileName<<"命令"<<m_cmd-3840;
//修改待发送字节数
m_bytesTobeWrite = m_totalBytes - m_client->write(m_outBlock);
m_outBlock.resize(0);
}
void Client::updateProgress(qint64 numBytes)
{
//发送文件内容
m_bytesWritten += numBytes;
if(m_bytesTobeWrite > 0)
{
//100<200?100:200
m_outBlock = m_localFile->read(qMin(m_bytesTobeWrite,m_payloadSize));
m_bytesTobeWrite -= m_client->write((m_outBlock));
m_outBlock.resize(0);
}
else
{
m_localFile->close();
}
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
ui->progressBar->setValue(m_bytesWritten*100/m_totalBytes);
if(m_bytesWritten == m_totalBytes)
{
qDebug()<<"发送总大小:"<<m_totalBytes<<"已发送"<<m_bytesWritten<<"待发送"<<m_bytesTobeWrite;
if(m_cmd == UPLOAD_FILE)
{
ui->labelUpTip->setText(QString("传送文件%1成功").arg(m_fileName));
}
if(m_cmd == SYN_FILE_LIST)
{
ui->labelPuTip->setText("请求同步列表已发送!");
}
if(m_cmd == DOWNLOAD_FILE)
{
ui->labelPuTip->setText("请求下载文件已发送!");
}
m_totalBytes = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
m_bytesReceived = 0;
m_fileNameSize = 0;
m_fileName = "";
m_cmd = 0;
m_outBlock.resize(0);
m_localFile->close();
}
}
void Client::onResquestFileList()
{
m_localFile = new QFile("");
if(!m_localFile->open(QFile::ReadOnly))
{
m_localFile->close();
}
QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_5_7);
QString curFile = "";
//构造临时文件头(总文件大小+文件名大小+消息类型+文件名)
sendOut<<qint64(0)<<qint64(0)<<qint64(0)<<curFile;
//计算文件总大小(总文件大小 = 文件大小 + 文件名 + sizeof(qint64)*3))
m_totalBytes += m_outBlock.size();
//将读写操作指向从头开始
sendOut.device()->seek(0);
//总大小和文件名大小和命令
sendOut<<m_totalBytes<<qint64((m_outBlock.size()-sizeof(qint64)*3))<<qint64(SYN_FILE_LIST);
m_cmd = SYN_FILE_LIST;
qDebug()<<"发送总大小"<<m_totalBytes<<"文件名大小"<<qint64((m_outBlock.size()-sizeof(qint64)*3))
<<"文件名称"<<m_fileName<<"命令"<<m_cmd-3840;
//修改待发送字节数
m_bytesTobeWrite = m_totalBytes - m_client->write(m_outBlock);
displayFileList();
}
void Client::displayFileList()
{
QString filePath = QApplication::applicationDirPath() + "/../../tcpClientFile/LocalFile";
QDir dir(filePath);
filePath = dir.absolutePath() + "/FILELIST.TXT";
QFile fileList(filePath);
if(!fileList.open(QFile::ReadOnly|QFile::Text))
{
qDebug()<<"Client::displayFileList :open filelist.txt error!"<<endl;
return;
}
QTextStream input(&fileList);
QString line;
ui->listWidget->clear();
while(input.readLineInto(&line)) //把所有文件名进行显示
{
//Qt中是编码格式是UTF-8-BOM
ui->listWidget->addItem(line);
}
}
QString Client::clearFilePath(QString filePath)
{
QString fileName = filePath.right(filePath.size() - filePath.lastIndexOf('/')-1);
return fileName;
}
void Client::onGetDownFileName()
{
m_downFileName = ui->listWidget->currentItem()->text();
}
void Client::onDownRequest()
{
if(m_downFileName.isEmpty())
{
ui->labelPuTip->setText("请先选择需要下载的文件");
return;
}
m_localFile = new QFile("");
if(!m_localFile->open(QFile::ReadOnly))
{
m_localFile->close();
}
QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_5_7);
QString curFile = m_downFileName;
//构造临时文件头(总文件大小+文件名大小+消息类型+文件名)
sendOut<<qint64(0)<<qint64(0)<<qint64(0)<<curFile;
//计算文件总大小(总文件大小 = 文件大小 + 文件名 + sizeof(qint64)*3))
m_totalBytes += m_outBlock.size();
//将读写操作指向从头开始
sendOut.device()->seek(0);
//总大小和文件名大小和命令
sendOut<<m_totalBytes<<qint64((m_outBlock.size()-sizeof(qint64)*3))<<qint64(DOWNLOAD_FILE);
m_cmd = DOWNLOAD_FILE;
qDebug()<<"发送总大小"<<m_totalBytes<<"文件名大小"<<qint64((m_outBlock.size()-sizeof(qint64)*3))
<<"文件名称"<<m_fileName<<"命令"<<m_cmd-3840;
//修改待发送字节数
m_bytesTobeWrite = m_totalBytes - m_client->write(m_outBlock);
}
void Client::onReceiveProgress()
{
QDataStream in(m_client);
in.setVersion(QDataStream::Qt_5_7);
// 如果已接收到的数据小于等于24个字节,保存文件头结构
if (m_bytesReceived<=sizeof(qint64)*3){
//先取24个字节(bytesAvailable:套接字中的数据)
if((m_client->bytesAvailable()>=sizeof(qint64)*3)&&(m_fileNameSize==0)){
// 接收数据总大小信息和文件名大小信息
in>>m_totalBytes>>m_fileNameSize>>m_cmd;
m_bytesReceived +=sizeof(qint64)*3;
}
if((m_client->bytesAvailable()>=m_fileNameSize)&&(m_fileNameSize!=0)){
// 接收文件名,并建立文件
in>>m_fileName;
m_bytesReceived+=m_fileNameSize;
if(m_cmd == UPLOAD_FILE)
{
ui->labelPuTip->setText(tr("接收文件 %1 …").arg(m_fileName));
//创建同名文件
QString filePath = QApplication::applicationDirPath() + "/../../tcpClientFile/LocalFile";
QDir dir(filePath);
filePath = dir.absolutePath() + QString("/%1").arg(m_fileName);
m_localFile = new QFile(filePath);
if (!m_localFile->open(QFile::WriteOnly)){
qDebug() << "Server::onReceiveProgress: open file error!";
return;
}
}
}
else{
return;
}
qDebug()<<"接收头部数据:"<<"总大小"<<m_totalBytes<<"头部大小"<<m_bytesReceived<<"命令"<<m_cmd-3840
<<"文件名"<<m_fileName;
}
// 如果接收的数据小于总数据,那么写入文件
if(m_bytesReceived<m_totalBytes) {
m_bytesReceived+=m_client->bytesAvailable();
m_inBlock = m_client->readAll();
//
m_localFile->write(m_inBlock);
m_inBlock.resize(0);
}
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
ui->progressBar->setValue(m_bytesReceived*100/m_totalBytes);
// 接收数据完成时,字段初始化,否则无法接收
if (m_bytesReceived==m_totalBytes){
qDebug()<<"接收文件内容:"<<"总大小"<<m_totalBytes<<"已接收"<<m_bytesReceived<<"文件名"<<m_fileName;
if(m_cmd == UPLOAD_FILE)
{
ui->labelPuTip->setText(tr("接收文件 %1 成功!").arg(m_fileName));
m_localFile->close();
m_totalBytes = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
m_bytesReceived = 0;
m_fileName = "";
m_downFileName = "";
m_cmd = 0;
m_payloadSize = 64 * 1024;
ui->progressBar->reset();
}
displayFileList();
}
}
服务端
Server.cpp
#include "Server.h"
#include "ui_Server.h"
#include <QDir>
Server::Server(QWidget *parent) :
QDialog(parent),
ui(new Ui::Server)
{
ui->setupUi(this);
setWindowTitle("文件服务器");
initServer();
connect(ui->btnListen,&QPushButton::clicked,this,&Server::onStart);
connect(ui->btnClose, &QPushButton::clicked, this , &Server::close);
connect(&m_tcpServer,&QTcpServer::newConnection,this,&Server::onAcceptConnected);
connect(&m_tcpServer,&QTcpServer::acceptError,this,&Server::displayError);
}
Server::~Server()
{
delete ui;
}
void Server::initServer()
{
m_fileNameSize = 0;
m_totalBytes = 0;
m_bytesReceived = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
//系统最大为64K,但可以自行修改系统设置
m_payLoadSize = 64 * 1024;
m_cmd = 0;
m_tcpSocket = new QTcpSocket(this);
}
void Server::onStart()
{
ui->labelTip->setText(QString("开始监听!"));
if(!m_tcpServer.listen(QHostAddress::Any,PORT))
{
ui->labelTip->setText(QString("%1").arg(m_tcpServer.errorString()));
m_tcpServer.close();
}
}
void Server::onAcceptConnected()
{
ui->labelTip->setText("连接成功!");
m_tcpSocket = m_tcpServer.nextPendingConnection();
//系统信号:readyRead/bytesWritten
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &Server::onReceiveProgress);
connect(m_tcpSocket, SIGNAL(bytesWritten(qint64)),this, SLOT(onUpdateSendProgress(qint64)));
}
void Server::onReceiveProgress()
{
//1.传输大文件时,界面不会卡住
qApp->processEvents();
QDataStream in(m_tcpSocket);
in.setVersion(QDataStream::Qt_5_7);
//2.如果已接收到的数据小于等于24个字节,保存文件头结构
if (m_bytesReceived<=sizeof(qint64)*3)
{
if((m_tcpSocket->bytesAvailable()>=sizeof(qint64)*3)&&(m_fileNameSize==0))
{
//(1)接收数据总大小和文件名大小和命令
in>>m_totalBytes>>m_fileNameSize>>m_cmd;
m_bytesReceived +=sizeof(qint64)*3;
}
if((m_tcpSocket->bytesAvailable()>=m_fileNameSize)&&(m_fileNameSize!=0))
{
//(2)接收文件名
in>>m_fileName;
m_bytesReceived+=m_fileNameSize;
//(3)解析命令:收到消息不处理,收到客户端上传命令建立文件
if(m_cmd == UPLOAD_FILE)
{
ui->labelTip->setText(tr("接收文件 %1 …").arg(m_fileName));
//创建同名文件
QString filePath = QApplication::applicationDirPath() + "/../../tcpServerFile/RemoteFile";
QDir dir(filePath);
filePath = dir.absolutePath() + QString("/%1").arg(m_fileName);
m_localFile = new QFile(filePath);
if (!m_localFile->open(QFile::WriteOnly)){
qDebug() << "Server::onReceiveProgress: open file error!";
return;
}
}
}
else
{
return;
}
qDebug()<<"接收头部数据:"<<"总大小"<<m_totalBytes<<"头部大小"<<m_bytesReceived<<"命令"<<m_cmd-3840
<<"文件名"<<m_fileName;
}
// 如果接收的数据小于总数据,那么写入文件
if(m_bytesReceived<m_totalBytes) {
m_bytesReceived+=m_tcpSocket->bytesAvailable();
m_inBlock = m_tcpSocket->readAll();
m_localFile->write(m_inBlock);
m_inBlock.resize(0);
}
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
ui->progressBar->setValue(m_bytesReceived*100/m_totalBytes);
// 接收数据完成时,字段初始化,防止数据污染
if (m_bytesReceived == m_totalBytes)
{
qDebug()<<"接收文件内容:"<<"总大小"<<m_totalBytes<<"已接收"<<m_bytesReceived<<"文件名"<<m_fileName;
if(m_cmd == UPLOAD_FILE)
{
ui->labelTip->setText(tr("接收文件 %1 成功!").arg(m_fileName));
m_localFile->close();
clearVar();
}
if(m_cmd == SYN_FILE_LIST)
{
ui->labelTip->setText("客户端请求文件同步列表");
clearVar();
getRemoteFileList();
sendFile();
}
if(m_cmd == DOWNLOAD_FILE)
{
ui->labelTip->setText("客户端请求下载文件");
clearVar();
sendFile();
}
}
}
void Server::sendFile()
{
if(m_cmd == SYN_FILE_LIST)
{
qDebug()<<"客户端请求远程文件列表";
QString fileList = QApplication::applicationDirPath() + "/../../tcpServerFile";
QDir dir(fileList);
fileList = dir.absolutePath() + "/FILELIST.TXT";
m_fileName = fileList;
}
if(m_cmd == DOWNLOAD_FILE)
{
qDebug()<<"客户端请求下载远程文件";
QString fileList = QApplication::applicationDirPath() + "/../../tcpServerFile/RemoteFile";
QDir dir(fileList);
fileList = dir.absolutePath() + QString("/%1").arg(m_fileName);
m_fileName = fileList;
}
qDebug()<<"发送文件名称:"<<m_fileName;
m_localFile = new QFile(m_fileName);
if(!m_localFile->open(QFile::ReadOnly))
{
qDebug()<<"文件打开错误";
}
m_totalBytes = m_localFile->size();
QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
sendOut.setVersion(QDataStream::Qt_5_7);
sendOut<<qint64(0)<<qint64(0)<<qint64(0)<<clearFilePath(m_fileName);
//计算文件总大小(总文件大小 = 文件大小 + 文件名大小 + sizeof(qint64)*3))
m_totalBytes += m_outBlock.size();
qDebug()<<"发送总文件大小:"<<m_totalBytes;
//将读写操作指向从头开始
sendOut.device()->seek(0);
//总长度和文件总长度和命令
sendOut<<m_totalBytes<<qint64((m_outBlock.size()-sizeof(qint64)*3))<<qint64(UPLOAD_FILE);
m_cmd = UPLOAD_FILE;
//修改待发送字节数
m_bytesTobeWrite = m_totalBytes - m_tcpSocket->write(m_outBlock);
m_outBlock.resize(0);
}
void Server::onUpdateSendProgress(qint64 numBytes)
{
//发送文件内容
m_bytesWritten += numBytes;
if(m_bytesTobeWrite > 0)
{
//100<200?100:200
m_outBlock = m_localFile->read(qMin(m_bytesTobeWrite,m_payLoadSize));
m_bytesTobeWrite -= m_tcpSocket->write((m_outBlock));
m_outBlock.resize(0);
}
else
{
m_localFile->close();
}
//更新回传进度条
ui->progressBar->setMinimum(0);
ui->progressBar->setMaximum(100);
ui->progressBar->setValue(m_bytesWritten*100/m_totalBytes);
if(m_bytesWritten == m_totalBytes)
{
ui->labelTip->setText(QString("回传文件%1成功").arg(m_fileName));
m_totalBytes = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
m_cmd = 0;
m_outBlock.resize(0);
m_localFile->close();
}
}
void Server::getRemoteFileList()
{
QString fileList = QApplication::applicationDirPath() + "/../../tcpServerFile/FILELIST.TXT";
QDir dir(fileList);
fileList = dir.absolutePath();
QFile f(fileList);
QTextStream out(&f);
if(!f.open(QFile::WriteOnly|QFile::Text))
{
qDebug()<<"write FILELIST.TXT error!"<<endl;
return;
}
QString remotePath = QApplication::applicationDirPath() + "/../../tcpServerFile/RemoteFile";
QDir d(remotePath);
remotePath = d.absolutePath();
QDir r(remotePath);
QStringList str;
str << "*";
QFileInfoList files = r.entryInfoList((QStringList)"*",
QDir::Files|QDir::Dirs,QDir::DirsFirst);
for(int i=0;i<files.count();i++)
{
QFileInfo tmpFileInfo = files.at(i);
QString fileName = tmpFileInfo.fileName();
//过滤"."和".."
if(fileName=="."||fileName=="..")
continue;
if(tmpFileInfo.isFile())
//写入TXT中
out << fileName << endl;
else
continue;
}
f.close();
}
void Server::clearVar()
{
m_fileNameSize = 0;
m_totalBytes = 0;
m_bytesReceived = 0;
m_bytesWritten = 0;
m_bytesTobeWrite = 0;
m_inBlock.resize(0);
m_outBlock.resize(0);
}
QString Server::clearFilePath(QString filePath)
{
QString fileName = filePath.right(filePath.size() - filePath.lastIndexOf('/')-1);
return fileName;
}
void Server::displayError(QAbstractSocket::SocketError socketError)
{
qDebug() << "Server::displayError " << socketError;
m_tcpServer.close();
ui->progressBar->reset();
}