毕业设计——基于C++和QT的文件传输系统(含客户端和服务端)

完整项目地址:https://download.csdn.net/download/lijunhcn/88453274

### 基于C++和QT的文件传输系统(含客户端和服务端)

采用C++、QT技术实现,项目分为服务端和客户端两部分
服务端项目结构:
核心源码:

#include "socketthread.h"
#include "dataexecution.h"
#include "widget.h"


SocketThread::SocketThread()
{

}

/*线程开始运行*/
void SocketThread::run()
{
    qDebug() <<"start";
    isGettingFile = false;  //开始不接收文件
    isDownloadingFile = false;

    //建立socket
    socket = new QTcpSocket();
    socket->setSocketDescriptor(ptr);
    connect(this, SIGNAL(startDownloadSignal()), this, SLOT(startDownload()));
    connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(updateDownloadProgress(qint64)));

    //文件传输的初始化
    upTotalBytes = 0;
    upBytesReceived = 0;
    upFileNameSize = 0;


    //循环接收事件
    ThreadRunning = true;
    while(ThreadRunning){
        //等待接收事件
        if(!isGettingFile){   //接收请求
            requestSize = 0;
            socket->waitForReadyRead(-1);
            QString requeType;      //接收请求类型
            QString str1;           //接收第一个字符串
            QString str2;           //接收第二个字符串
            QDataStream in(socket);
            //设置数据流版本,这里要和服务器端相同
            in.setVersion(QDataStream::Qt_5_6);
            if(requestSize == 0){//如果是刚开始接收数据
                //判断接收的数据是否大于四字节,也就是文件的大小信息所占的空间
                //如果是则保存到requestSize变量中,否则直接返回,继续接收数据
                if(socket->bytesAvailable() < (int)sizeof(quint64)) return;
                in >> requestSize;
            }
            //如果没有得到全部的数据,则返回,继续接收数据
            if(socket->bytesAvailable() < requestSize) return;
            //将接收到的数据存放在变量中
            in >> requeType >> str1 >> str2;
            //显示接收到的数据
            qDebug() <<requeType << str1 << str2;

            //对收到的请求进行处理
            if(requeType == LOGOUT){       //登出
                logoutExecution(str1);
            }
            else if(requeType == LOGINREQ){  //登录请求
                loginExecution(str1, str2);     //处理登录请求
            }
            else if(requeType == REGIREQ){  //注册请求
                registerExecution(str1, str2);
            }
            else if(requeType == UPDATE){   //上传请求
                int realNameIndex = str2.lastIndexOf("/");
                QString realName = str2.right(str2.length ()-realNameIndex-1);  //取真正文件名
                QString searchFileName = "./files/" + realName;
                QFile file(searchFileName);
                if(file.exists()){      //文件存在,拒绝上传
                    qDebug() << searchFileName << tr("存在");
                    updateReject();
                    QString showMess = "[-拒绝上传-]服务器拒绝" + str1 + "上传" + realName;
                    emit showMessThreadToServer(showMess);
                }
                else{       //文件不存在
                    qDebug() << searchFileName << tr("不存在");
                    //允许客户端上传
                    updateExecution(str1, str2);
                }

            }
            else if(requeType == SEARCH){   //查找请求
                searchExecution(str1, str2);
            }
            else if(requeType == DOWNSTART){        //开始发送文件
                downStartExecution(str1, str2);
            }
            else if(requeType == EXITAPP){  //退出应用程序
                socket->close();    //关闭socket
                ThreadRunning = false;       //关闭此线程
            }
        }//if
        else{   //文件传输
            socket->waitForReadyRead(-1);
            QDataStream in(socket);
            in.setVersion(QDataStream::Qt_5_6);
            //如果接收到的数据小于16个字节,保存到来的文件头结构
            if(upBytesReceived <= sizeof(qint64) * 2){
                if((socket->bytesAvailable() >= sizeof(qint64) * 2) && (upFileNameSize == 0)){
                    //接收数据总大小信息和文件名大小信息
                    in >> upTotalBytes >> upFileNameSize;
                    upBytesReceived += sizeof(qint64) * 2;
                }
                if((socket->bytesAvailable() >= upFileNameSize) && (upFileNameSize != 0)){
                    //接收文件名,并建立文件
                    in >> upFileName;
                    upFileName = "./files/" + upFileName;   //将上传文件装入files文件夹中
                    qDebug() << upFileName;
                    upBytesReceived += upFileNameSize;
                    upLocalFile = new QFile(upFileName);
                    if(!upLocalFile->open(QFile::WriteOnly)){   //若文件不存在,会自动创建一个
                        qDebug() << "Server: open file error!";
                        return;
                    }
                }
                else{
                    return;
                }
            }
            //文件未接收完时继续接收
            if(upBytesReceived < upTotalBytes){
                upBytesReceived += socket->bytesAvailable();
                upBlock = socket->readAll();
                upLocalFile->write(upBlock);
                upBlock.resize(0);
            }

            //接收数据完成时
            if(upBytesReceived == upTotalBytes){
                upLocalFile->close();
                upTotalBytes = 0;
                upBytesReceived = 0;
                upFileNameSize = 0;
                isGettingFile = false;  //状态设置为不接收文件
            }
        }//if
    }//while
}

/*向客户端发送消息*/
void SocketThread::sendMessToClient(QString Mess)
{
    QByteArray sendBlock;    //用来存放请求信息
    sendBlock.resize(0);
    QDataStream out(&sendBlock, QIODevice::WriteOnly);
    //设置数据流的版本,客户端和服务器使用的版本要相同
    out.setVersion(QDataStream::Qt_5_6);//版本
    out << (quint64)0;//因为在写入数据以前可能不知道实际数据的大小,所以要先在数据块的最前面留八个字节的位置,以便以后填写数据大小
    out << Mess;      //输入实际数据
    out.device()->seek(0);//跳转到数据块头部
    out << (quint64)(sendBlock.size() - sizeof(quint64));//填写信息大小
    socket->write(sendBlock); //发送
    qDebug() << "send" << Mess;
}

/*登出处理*/
void SocketThread::logoutExecution(QString str1)
{
    QString id = str1;
    //抛出界面输出信息的信号
    QString showMess = "[-登出-]" + id + "登出成功";
    emit showMessThreadToServer(showMess);
}

/*处理登录请求*/
void SocketThread::loginExecution(QString str1, QString str2)
{
    QString id = str1;
    QString pass = str2;
    int result = loginSearchDataModel(id, pass);
    if(result == 1){        //登录成功
        qDebug() << id <<"登录成功";

        //抛出界面输出信息的信号
        QString showMess = "[-登录-]" + id + "登录成功";
        emit showMessThreadToServer(showMess);

        sendMessToClient(LOGINSUCCESS);     //向客户端发送登录成功信息
    }
    else if(result == 2){   //登录失败,密码错误
        qDebug() << id <<"密码错误";
        sendMessToClient(LOGINPASSERROR);   //向客户端发送登录密码错误的信息
    }
    else{                   //登录失败,查无此人
        qDebug() << id <<"查无此人";
        sendMessToClient(LOGINIDNON);       //向客户端发送查无此人的信息
    }
}

/*处理注册请求*/
void SocketThread::registerExecution(QString str1, QString str2)
{
    QString id = str1;
    QString pass = str2;
    bool exit = regiSearchDataModel(str1);    //查询是否重复,已重复返回false
    if(id == ""){   //ID为空
        sendMessToClient(REGIIDBLANKERROR);     //向客户端发送ID为空信息
    }
    else if(pass == ""){    //密码为空
        sendMessToClient(REGIPASSBLANKERROR);   //向客户端发送密码为空的信息
    }
    else if(exit == false){     //ID已存在
        sendMessToClient(REGIIDEXIT);           //向客户端发送ID已存在信息
    }
    else{       //注册成功
        sendMessToClient(REGISUCCESS);          //向客户端发送注册成功信息
        addDataModel(id, pass); //将注册信息添加到数据库

        QString showMess = "[-注册-]" + id + "注册并登录成功";   //抛出界面输出信息的信号
        emit showMessThreadToServer(showMess);
    }

}

/*处理上传请求*/
void SocketThread::updateExecution(QString str1, QString str2)
{
    QString id = str1;
//    QString upName = str2;
    int realNameIndex = str2.lastIndexOf("/");
    QString realName = str2.right(str2.length ()-realNameIndex-1);  //取真正文件名
    QString showMess = "[-上传文件-]" + id + "上传" + realName;
    emit showMessThreadToServer(showMess);

    isGettingFile = true;   //设置为接收文件标记
    sendMessToClient(UPSTART);  //向客户端发送开始上传信息
}

/*文件已存在,拒绝上传请求*/
void SocketThread::updateReject()
{
    sendMessToClient(UPREJ);    //向客户端发送拒绝上传信息
}

/*查找请求处理*/
void SocketThread::searchExecution(QString str1, QString str2)
{
    QString id = str1;
    if(str2 == ""){     //如果查找的文件名为空
        sendMessToClient(FILENAMENON);  //向客户端发送文件名为空信息
    }
    else{       //查找的文件名不为空
        QString searchFileName = "./files/" + str2;
        QFile file(searchFileName);
        if(file.exists()){      //文件存在
            qDebug() << searchFileName << tr("存在");
            sendMessToClient(FILEEXIST);    //向客户端发送文件训在信息
        }
        else{       //文件不存在
            qDebug() << searchFileName << tr("不存在");
            sendMessToClient(FILENONEXIST); //向客户端发送文件不存在信息
        }
    }
}

/*开始下载处理*/
void SocketThread::downStartExecution(QString str1, QString str2)
{
    QString id = str1;
    QString dFileName = str2;
    downFileName = "./files/" + dFileName;
    QString showMess = "[-下载-]" + id + "下载" + dFileName;
    emit showMessThreadToServer(showMess);

    qDebug() << "start download" << dFileName;
    downPerSize = 64*1024;   //64kb
    downTotalBytes = 0;
    downBytesWritten = 0;
    downBytesToWrite = 0;
    emit startDownloadSignal(); //发送上传文件信号
    qDebug() << "here";
}

/*开始下载文件时服务器开始传输*/
void SocketThread::startDownload()
{
    fileToDownload = new QFile(downFileName);
    if(!fileToDownload->open(QFile::ReadOnly)){
        qDebug() <<"server: open file error!";
        return;
    }
    //获取文件大小
    downTotalBytes = fileToDownload->size();
    QDataStream sendOut(&downBlock, QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_6);
    QString currentFileName = downFileName.right(downFileName.size() - downFileName.lastIndexOf('/') - 1);
    //保留总大小信息空间、文件名大小信息空间,然后输入文件名
    sendOut << qint64(0) << qint64(0) << currentFileName;
    //这里的总大小是总大小信息、文件名大小信息、文件名和实际文件大小的总和
    downTotalBytes += downBlock.size();  //要发送的整个数据的大小(文件头结构+实际文件大小)  放在数据流最开始,占用第一个qint(64)的空间
    sendOut.device()->seek(0);
    //返回outBlock的开始,用实际的大小信息代替两个qint(0)空间
    sendOut << downTotalBytes <<qint64((downBlock.size() - sizeof(qint64) * 2));
    //发送完文件头结构后剩余的数据大小

    downBytesToWrite = downTotalBytes - socket->write(downBlock);
    downBlock.resize(0);//outBlock是暂存数据的,最后要将其清零
    qDebug() <<"trans";
    isDownloadingFile = true;
}

/*文件下载时服务器传输过程*/
void SocketThread::updateDownloadProgress(qint64 numBytes)
{
    if(isDownloadingFile){
        //已经发送数据的大小
        downBytesWritten += (int)numBytes;
        //如果已经发送了数据
        if(downBytesToWrite > 0){
            //每次发送payloadSize大小的数据,这里设置为64KB,如果剩余的数据不足64KB就发送剩余数据的大小
            downBlock = fileToDownload->read(qMin(downBytesToWrite, downPerSize));
            //发送完一次数据后还剩余数据的大小

            downBytesToWrite -= (int)socket->write(downBlock);
            //emit hasWritten(haswrite);

            //socket->waitForBytesWritten();
            //清空发送缓冲区
            downBlock.resize(0);
        }
        else{   //如果没有发送任何数据,则关闭文件
            fileToDownload->close();
        }
        //如果发送完毕
        if(downBytesWritten == downTotalBytes){
            fileToDownload->close();
            isDownloadingFile = false;    //退出文件传输状态
        }
    }
}

客户端项目结构:
核心源码:

#include "client.h"
#include "ui_client.h"
#include <QGridLayout>
#include <QCloseEvent>
#include <QMessageBox>
#include <QDebug>
#include <QtNetwork>
#include <QFileDialog>
#include <QFile>

Client::Client(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Client)
{
    ui->setupUi(this);
    //添加样式
    qssFile = new QFile(":/qss/myStyleSheetB.qss",this);
    qssFile->open(QFile::ReadOnly);
    QString styleSheet = tr(qssFile->readAll());
    qApp->setStyleSheet(styleSheet);
    qssFile->close();

     //栅格化初始布局
    setMyLayout();
     //进入登录注册界面
    loginView();

    tcpClient = new QTcpSocket(this);
    tcpClient->connectToHost(QHostAddress::LocalHost,6666);

    //先不进行文件传输
    isUploadingFile = false;

    //接收文件初始化
    isDownloadingFile = false;
    downTotalBytes = 0;
    downBytesReceived = 0;
    downFileNameSize = 0;

    //当有可读数据时,发射readyread信号
    connect(tcpClient, &QTcpSocket::readyRead, this, &Client::readMessage);
    //连接错误
    connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(disPlayError(QAbstractSocket::SocketError)));//发生错误时error信号
    //发射开始上传信息好后开始上传
    connect(this, SIGNAL(startUploadSignal()), this, SLOT(startUpload()));
    //上传过程中更新上传进度
    connect(tcpClient, SIGNAL(bytesWritten(qint64)), this, SLOT(updateUploadProgress(qint64)));

    qDebug() <<"ok";
}

Client::~Client()
{
    delete tcpClient;
    delete ui;
}

/*-------------------------页面布局模块--------------------------*/

/*栅格化初始布局*/
void Client::setMyLayout()
{
    QGridLayout *layout = new QGridLayout;
    layout->addWidget(ui->idLabel,1,0,1,2);
    layout->addWidget(ui->idLineEdit,1,2,1,4);
    layout->addWidget(ui->passLabel,2,0,1,2);
    layout->addWidget(ui->passLineEdit,2,2,1,4);
    layout->addWidget(ui->regiButton,3,0,1,3);
    layout->addWidget(ui->loginButton,3,3,1,3);
    layout->addWidget(ui->stateLabel,4,0,1,2);
    layout->addWidget(ui->contentLabel,4,2,1,4);
    layout->addWidget(ui->loginStatusLabel,5,0,1,4);
    layout->addWidget(ui->exitButton,5,4,1,2);
    layout->addWidget(ui->actLine,6,0,1,6);
    layout->addWidget(ui->upWarnLabel,7,2,1,2);
    layout->addWidget(ui->openButton,8,0,1,3);
    layout->addWidget(ui->upButton,8,3,1,3);
    layout->addWidget(ui->filenameLabel,9,0,1,6);
    layout->addWidget(ui->upBar,10,0,1,6);
    layout->addWidget(ui->sepLine,11,0,1,6);
    layout->addWidget(ui->downWarnLabel,12,2,1,2);
    layout->addWidget(ui->searchEdit,13,0,1,4);
    layout->addWidget(ui->searchButton,13,4,1,2);
    layout->addWidget(ui->downLabel,14,0,1,6);
    layout->addWidget(ui->downButton,15,0,1,3);
    layout->addWidget(ui->unDownButton,15,3,1,3);
    layout->addWidget(ui->downBar,16,0,1,6);
    layout->setHorizontalSpacing(20);   //控件间横向距离
    layout->setVerticalSpacing(30);     //控件间纵向距离
    layout->setMargin(30);              //设置内边距
    setLayout(layout);
}

/*登录界面*/
void Client::loginView()
{
    //要隐藏的控件
    ui->loginStatusLabel->hide();
    ui->exitButton->hide();
    ui->actLine->hide();
    ui->openButton->hide();
    ui->upButton->hide();
    ui->filenameLabel->hide();
    ui->upBar->hide();
    ui->sepLine->hide();
    ui->searchEdit->hide();
    ui->searchButton->hide();
    ui->downLabel->hide();
    ui->downButton->hide();
    ui->downBar->hide();
    ui->upWarnLabel->hide();
    ui->downWarnLabel->hide();
    ui->unDownButton->hide();
    //要显示的控件
    ui->idLabel->show();
    ui->idLineEdit->show();
    ui->passLabel->show();
    ui->passLineEdit->show();
    ui->loginButton->show();
    ui->regiButton->show();
    ui->stateLabel->show();
    ui->contentLabel->show();
    //设置大小
    this->resize(350,230);

}

/*客户端操作界面*/
void Client::actView()
{
    //要隐藏的控件
    ui->idLabel->hide();
    ui->idLineEdit->hide();
    ui->passLabel->hide();
    ui->passLineEdit->hide();
    ui->loginButton->hide();
    ui->regiButton->hide();
    ui->stateLabel->hide();
    ui->contentLabel->hide();
    //要显示的控件
    ui->loginStatusLabel->show();
    ui->actLine->show();
    ui->openButton->show();
    ui->upButton->show();
    ui->filenameLabel->show();
    ui->upBar->show();
    ui->sepLine->show();
    ui->searchEdit->show();
    ui->searchButton->show();
    ui->downLabel->show();
    ui->downButton->show();
    ui->downBar->show();
    ui->exitButton->show();
    ui->upWarnLabel->show();
    ui->downWarnLabel->show();
    ui->unDownButton->show();
    //控件初始化
    ui->downBar->reset();
    ui->upBar->reset();
    ui->filenameLabel->setText(tr("当前未选择文件"));
    ui->downLabel->setText(tr("当前未搜索文件"));
    ui->searchEdit->setText(tr(""));
    //上传下载按钮设置无效
    ui->upButton->setEnabled(false);
    ui->downButton->setEnabled(false);
    ui->unDownButton->setEnabled(false);
    //设置大小
    this->resize(350,420);
}


/*-------------------------页面布局模块结束--------------------------*/

/*-------------------------操作模块--------------------------------*/

/*发送消息的函数
 * Mess -- 消息指令
 * str1 -- 第一内容字符串
 * str2 -- 第二内容字符串
 * */
void Client::sendMessToServer(QString Mess, QString str1, QString str2){
    QByteArray requestBlock;    //用来存放请求信息
    QDataStream out(&requestBlock, QIODevice::WriteOnly);
    //设置数据流的版本,客户端和服务器使用的版本要相同
    out.setVersion(QDataStream::Qt_5_6);//版本
    out << (quint64)0;//因为在写入数据以前可能不知道实际数据的大小,所以要先在数据块的最前面留四个字节位置,以便以后填写数据大小
    out << Mess << str1 << str2;        //输入实际数据
    out.device()->seek(0);//跳转到数据块头部
    out << (quint64)(requestBlock.size() - sizeof(quint64));//填写信息大小
    tcpClient->write(requestBlock); //发送
    qDebug() <<"send" << Mess << str1 << str2;
    requestBlock.resize(0);
}

/*点击x按钮*/
void Client::closeEvent(QCloseEvent *event)
{
    QMessageBox::StandardButton button;
    if(isUploadingFile){    //如果正在传输文件,提示有文件正在上传
        button = QMessageBox::warning(this, tr("警告"), QString(tr("有文件正在上传,无法退出程序")),
                                      QMessageBox::Cancel);
        event->ignore();  //忽略退出信号,程序继续运行
    }
    else if(isDownloadingFile){ //如果正在下载文件,提示有文件正在下载
        button = QMessageBox::warning(this, tr("警告"), QString(tr("有文件正在下载,无法退出程序")),
                                      QMessageBox::Cancel);
        event->ignore();  //忽略退出信号,程序继续运行
    }
    else{
        button = QMessageBox::question(this, tr("退出程序"), QString(tr("确认退出程序?")),
                                      QMessageBox::Yes | QMessageBox::No);
        if (button == QMessageBox::No) {
            event->ignore();  //忽略退出信号,程序继续运行
        }
        else if (button == QMessageBox::Yes) {
            sendMessToServer(EXITAPP, MESSNULL, MESSNULL);  //向服务器发送退出客户端信息
            tcpClient->close();
            event->accept();  //接受退出信号,程序退出
            qDebug() <<"exit";
        }
    }
}

/*点击登录按钮,向服务器发送请求*/
void Client::on_loginButton_clicked()
{
    QString id = ui->idLineEdit->text();        //获取账户
    QString pass = ui->passLineEdit->text();    //获取密码
    sendMessToServer(LOGINREQ, id, pass);       //向服务器发送登录请求
}

/*点击注册按钮*/
void Client::on_regiButton_clicked()
{
    QString id = ui->idLineEdit->text();        //获取账户
    QString pass = ui->passLineEdit->text();    //获取密码
    sendMessToServer(REGIREQ, id, pass);        //向服务器发送注册请求
}

/*点击退出登录按钮*/
void Client::on_exitButton_clicked()
{
    QString id = ui->idLineEdit->text();        //获取账户
    loginView();    //显示登录界面
    ui->idLineEdit->setText("");    //登录输入行清空
    ui->passLineEdit->setText("");
    ui->contentLabel->setText(tr("请注册或登录"));
    sendMessToServer(LOGOUT, id, MESSNULL);     //向客户端发送退出登录请求
}

/*-------------------------操作模块结束--------------------------------*/

/*-------------------------接受信息模块-------------------------------*/

/*接收信息*/
void Client::readMessage()
{
    if(!isDownloadingFile){     //不是接收文件状态
        QString getMess;       //接收到的消息
        messageSize = 0;    //初始化信息大小
        QDataStream in(tcpClient);
        in.setVersion(QDataStream::Qt_5_6);
        if(messageSize == 0){   //如果是刚开始接收数据
            //判断接收的数据是否大于四字节,也就是文件的大小信息所占的空间
            //如果是则保存到blockSize变量中,否则直接返回,继续接收数据
            if(tcpClient->bytesAvailable() < (int)sizeof(quint64)) return;
            in >> messageSize;
        }
        //如果没有得到全部的数据,则返回,继续接收数据
        if(tcpClient->bytesAvailable() < messageSize) return;
        //将接收到的数据存放在变量中
        in >> getMess;
        qDebug() << "receive" << getMess;

        //根据接收信息进行处理
        /*登录处理*/
        if(getMess == LOGINSUCCESS){    //登录成功
            actView();      //登陆成功后显示处理界面
            QString statusShow = "用户:" + ui->idLineEdit->text() + " 正在登录";
            ui->loginStatusLabel->setText(statusShow);  //显示用户登录信息
        }
        else if(getMess == LOGINPASSERROR){ //登录密码错误
            ui->passLineEdit->setText("");  //密码框重置
            ui->contentLabel->setText(tr("密码错误,请重新输入"));
        }
        else if(getMess == LOGINIDNON){   //登录用户不存在
            ui->idLineEdit->setText("");    //输入框清空
            ui->passLineEdit->setText("");
            ui->contentLabel->setText(tr("用户不存在,请重新输入"));
        }

        /*注册处理*/
        else if(getMess == REGISUCCESS){      //注册成功
            actView();      //登陆成功后显示处理界面
            QString statusShow = "用户:" + ui->idLineEdit->text() + " 正在登录";
            ui->loginStatusLabel->setText(statusShow);  //显示用户登录信息
        }
        else if(getMess == REGIIDBLANKERROR){   //ID为空错误
            ui->idLineEdit->setText("");    //输入框清空
            ui->passLineEdit->setText("");
            ui->contentLabel->setText(tr("用户名不能为空"));
        }
        else if(getMess == REGIPASSBLANKERROR){ //密码为空错误
            ui->passLineEdit->setText("");
            ui->contentLabel->setText(tr("密码不能为空"));
        }
        else if(getMess == REGIIDEXIT){     //ID已存在
            ui->idLineEdit->setText("");    //输入框清空
            ui->passLineEdit->setText("");
            ui->contentLabel->setText(tr("用户名已存在"));
        }
        else if(getMess == UPSTART){    //开始上传文件
            qDebug() << "start update" << upFileName;
            upPerSize = 64*1024;   //64kb
            upTotalBytes = 0;
            upBytesWritten = 0;
            upBytesToWrite = 0;
            emit startUploadSignal(); //发送上传文件信号
        }
        else if(getMess == UPREJ){      //文件已存在,重新上传
            ui->filenameLabel->setText(tr("此文件已存在,请重新上传"));
            ui->openButton->setEnabled(true);
            ui->searchButton->setEnabled(true);
            ui->exitButton->setEnabled(true);
        }
        else if(getMess == FILEEXIST){  //查询的文件存在
            QString searchFileName = ui->searchEdit->text();
            showDownFileName = searchFileName;  //要下载的文件名是查找到的文件名
            int realNameIndex = searchFileName.lastIndexOf("/");
            QString realName = searchFileName.right(searchFileName.length ()-realNameIndex-1);
            QString labelShow = realName + " 存在";
            ui->downLabel->setText(labelShow);      //显示文件存在,接下来选择是否下载
            ui->downButton->setEnabled(true);
            ui->unDownButton->setEnabled(true);     //选择下载或不进行下载
            ui->exitButton->setEnabled(false);
            ui->openButton->setEnabled(false);
            ui->searchButton->setEnabled(false);
        }
        else if(getMess == FILENONEXIST){   //查询的文件不存在
            QString searchFileName = ui->searchEdit->text();    //获取要查找的文件名
            int realNameIndex = searchFileName.lastIndexOf("/");
            QString realName = searchFileName.right(searchFileName.length ()-realNameIndex-1);  //取真正文件名
            QString labelShow = realName + " 不存在";
            ui->downLabel->setText(labelShow);
        }
        else if(getMess == FILENAMENON){    //查询的文件名为空
            ui->downLabel->setText(tr("查询的文件名不能为空"));
        }
    }

    //接收文件传输状态
    else{
        QDataStream in(tcpClient);
        in.setVersion(QDataStream::Qt_5_6);
        //如果接收到的数据小于16个字节,保存到来的文件头结构
        if(downBytesReceived <= sizeof(qint64) * 2){
            if((tcpClient->bytesAvailable() >= sizeof(qint64) * 2) && (downFileNameSize == 0)){
                //接收数据总大小信息和文件名大小信息
                in >> downTotalBytes >> downFileNameSize;
                qDebug() << "downTotalBytes" << downTotalBytes << "downFileNameSize" << downFileNameSize;
                downBytesReceived += sizeof(qint64) * 2;
            }
            if((tcpClient->bytesAvailable() >= downFileNameSize) && (downFileNameSize != 0)){
                //接收文件名,并建立文件
                in >> downFileName;
                ui->downLabel->setText(tr("下载文件 %1").arg(downFileName));
                downFileName = "./files/" + downFileName;   //将下载文件装入files文件夹中
                qDebug() << downFileName;
                downBytesReceived += downFileNameSize;
                downLocalFile = new QFile(downFileName);
                if(!downLocalFile->open(QFile::WriteOnly)){   //若文件不存在,会自动创建一个
                    qDebug() << "Client: open file error!";
                    return;
                }
            }
            else{
                return;
            }
        }
        //文件未接收完时继续接收
        if(downBytesReceived < downTotalBytes){
            downBytesReceived += tcpClient->bytesAvailable();
            downBlock = tcpClient->readAll();
            downLocalFile->write(downBlock);    //写入文件
            downBlock.resize(0);
        }

        //更新进度条
        ui->downBar->setMaximum(downTotalBytes);
        ui->downBar->setValue(downBytesReceived);

        //接收数据完成时
        if(downBytesReceived == downTotalBytes){
            downLocalFile->close();
            downTotalBytes = 0;
            downBytesReceived = 0;
            downFileNameSize = 0;
            QString labelShow = "已完成下载 " + showDownFileName;
            ui->downLabel->setText(labelShow);
            ui->exitButton->setEnabled(true);
            ui->openButton->setEnabled(true);
            ui->searchButton->setEnabled(true);
            ui->searchEdit->setText(tr(""));
            isDownloadingFile = false;  //状态设置为不接收文件
        }
    }


}

/*接收信息错误处理*/
void Client::disPlayError(QAbstractSocket::SocketError)
{
    qDebug() << tcpClient->errorString();
}

/*-------------------------接受信息模块结束-------------------------------*/

/*------------------------------上传模块------------------------------------*/

/*点击打开文件按钮*/
void Client::on_openButton_clicked()
{
    ui->upBar->reset();
    ui->filenameLabel->setText(tr("状态:等待打开文件!"));
    openFile();     //打开文件
}

/*打开要上传的文件*/
void Client::openFile()
{
    upFileName = QFileDialog::getOpenFileName(this);
    if(!upFileName.isEmpty()){
        ui->upButton->setEnabled(true);     //上传按钮生效
        int realNameIndex = upFileName.lastIndexOf("/");
        QString realName = upFileName.right(upFileName.length ()-realNameIndex-1);  //取真正文件名
        ui->filenameLabel->setText(tr("文件名: %1").arg(realName));
        qDebug() << upFileName;
    }
}

/*开始上传按钮*/
void Client::on_upButton_clicked()
{
    //给服务器发送上传请求
    QString id = ui->idLineEdit->text();
    sendMessToServer(UPDATE, id, upFileName);  //向服务器发送开始上传请求

    ui->upButton->setEnabled(false);    //文件开始传输时上传按钮失效
    ui->openButton->setEnabled(false);  //打开文件按钮失效
    ui->exitButton->setEnabled(false);  //退出登录按钮失效
    ui->searchButton->setEnabled(false);    //查找按钮失效
    ui->downButton->setEnabled(false);  //下载按钮失效
}

/*开始上传文件*/
void Client::startUpload()
{
    fileToUpdate = new QFile(upFileName);
    if(!fileToUpdate->open(QFile::ReadOnly)){
        qDebug() <<"client: open file error!";
        return;
    }
    //获取文件大小
    upTotalBytes = fileToUpdate->size();
    QDataStream sendOut(&upBlock, QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_6);
    QString currentFileName = upFileName.right(upFileName.size() - upFileName.lastIndexOf('/') - 1);
    //保留总大小信息空间、文件名大小信息空间,然后输入文件名
    sendOut << qint64(0) << qint64(0) << currentFileName;
    //这里的总大小是总大小信息、文件名大小信息、文件名和实际文件大小的总和
    upTotalBytes += upBlock.size();  //要发送的整个数据的大小(文件头结构+实际文件大小)  放在数据流最开始,占用第一个qint(64)的空间
    sendOut.device()->seek(0);
    //返回outBlock的开始,用实际的大小信息代替两个qint(0)空间
    sendOut << upTotalBytes <<qint64((upBlock.size() - sizeof(qint64) * 2));
    //发送完文件头结构后剩余的数据大小
    upBytesToWrite = upTotalBytes - tcpClient->write(upBlock);
    upBlock.resize(0);  //outBlock是暂存数据的,最后要将其清零
    qDebug() <<"transenddd";
    isUploadingFile = true;     //修改正在上传文件标记
    int realNameIndex = upFileName.lastIndexOf("/");
    QString realName = upFileName.right(upFileName.length ()-realNameIndex-1);  //取真正文件名
    ui->filenameLabel->setText(tr("正在上传: %1").arg(realName));
}

/*文件传输过程*/
void Client::updateUploadProgress(qint64 numBytes)
{
    if(isUploadingFile){
        //已经发送数据的大小
        upBytesWritten += (int)numBytes;
        //如果已经发送了数据
        if(upBytesToWrite > 0){
            //每次发送payloadSize大小的数据,这里设置为64KB,如果剩余的数据不足64KB就发送剩余数据的大小
            upBlock = fileToUpdate->read(qMin(upBytesToWrite, upPerSize));
            //发送完一次数据后还剩余数据的大小
            upBytesToWrite -= (int)tcpClient->write(upBlock);
            //清空发送缓冲区
            upBlock.resize(0);
        }
        else{   //如果没有发送任何数据,则关闭文件
            fileToUpdate->close();
        }
        //更新进度条
        ui->upBar->setMaximum(upTotalBytes);
        ui->upBar->setValue(upBytesWritten);
        //如果发送完毕
        if(upBytesWritten == upTotalBytes){
            int realNameIndex = upFileName.lastIndexOf("/");
            QString realName = upFileName.right(upFileName.length ()-realNameIndex-1);  //取真正文件名
            ui->filenameLabel->setText(tr("传送文件 %1 成功").arg(realName));
            fileToUpdate->close();
            isUploadingFile = false;    //退出文件传输状态
            ui->openButton->setEnabled(true);   //文件传输完成后打开文件按钮有效
            ui->exitButton->setEnabled(true);   //退出登录按钮有效
            ui->searchButton->setEnabled(true); //查找按钮有效
        }
    }
}

/*------------------------------上传模块结束----------------------------------*/

/*------------------------------下载模块-------------------------------------*/

/*查找服务器文件*/
void Client::on_searchButton_clicked()
{
    QString id = ui->idLineEdit->text();
    QString searchFileName = ui->searchEdit->text();    //获取要查找的文件名
    ui->downBar->reset();

    sendMessToServer(SEARCH, id, searchFileName);       //给服务器发送查找请求
}

/*查找到后进行下载的按钮*/
void Client::on_downButton_clicked()
{
    QString id = ui->idLineEdit->text();
    sendMessToServer(DOWNSTART, id, showDownFileName);  //给服务器发送下载请求

    isDownloadingFile = true;       //客户端进入接收下载文件状态
    ui->unDownButton->setEnabled(false);
    ui->downButton->setEnabled(false);
}

/*查找到后不进行下载的按钮*/
void Client::on_unDownButton_clicked()
{
    QString labelShow = "已取消下载 " + showDownFileName;
    ui->downLabel->setText(labelShow);
    ui->downButton->setEnabled(false);
    ui->unDownButton->setEnabled(false);
    ui->exitButton->setEnabled(true);
    ui->openButton->setEnabled(true);
    ui->searchButton->setEnabled(true);
    ui->searchEdit->setText(tr(""));
}

猜你喜欢

转载自blog.csdn.net/lijunhcn/article/details/135163199