美丽的神话 发表于 2024-8-6 19:10:46

Qt:多线程TCP服务器客户端双向文件互传,Server端和Client端分离(带进度

Client 界面:

https://i-blog.csdnimg.cn/blog_migrate/7e00bff460412b4bcf20c7bacc980f7c.png
Server 界面:

https://i-blog.csdnimg.cn/blog_migrate/ebd1ab55d6d90411b667c8d08315b6c6.png

Server 利用:

输入端口号后,点击启动监听
Client 利用:

1.毗连服务器

输入IP(服务器的IP所在)和端口号后,点击毗连按钮,毗连成功如下图所示:
https://i-blog.csdnimg.cn/blog_migrate/9a3c76612621b6657fd0cde17f777c39.png
2.向服务器发送文件

2.1 点击“选择当地文件”按钮,选择一个当地的文件。

2.2 点击“选择存储在服务器的目录”,选择发送的文件将要存储在服务器的哪个目录中。(此功能可直接在一台电脑上测试,当多台电脑的时候可以将目录改为服务器的现实目录所在)

2.3 点击“发送文件”按钮,即可开始向服务器传输文件,传输成功后客户端会有提示框。

3.下载服务器的文件(服务器向客户端发送文件)

3.1 点击“选择要下载的文件”按钮,选择想要下载的服务器下的文件(此功能可直接在一台电脑上测试,当多台电脑的时候可以将目录改为服务器的现实文件所在)。

3.2 点击“选择下载目录”按钮,选择下载文件的存储位置。

3.3 点击“下载文件”按钮,即可开始下载服务器的文件,下载成功后客户端会有提示框。



Server-Mainwindow焦点代码

    //设置进度条
    ui->progressBar->setRange(0,100);
    ui->progressBar->setValue(0);


    QWidget *progressWidget = new QWidget(this);
    progressWidget->setLayout(new QHBoxLayout());
    progressWidget->layout()->addWidget(ui->progressBar);

    // 创建 QStatusBar
    // 将小部件添加到 QStatusBar
    ui->statusbar->addPermanentWidget(progressWidget);
    // 设置主窗口的状态栏
    setStatusBar(ui->statusbar);
    ui->statusbar->setSizeGripEnabled(false);



    //创建线程对象
    QThread *thread = new QThread;
    //创建任务对象
    RecvFile *recvWork = new RecvFile;
    //将任务对象移到子线程中
    recvWork->moveToThread(thread);

    connect(this,&MainWindow::startListen,recvWork,&RecvFile::startListen);
    //更新进度条
    connect(recvWork,&RecvFile::curPercent,ui->progressBar,&QProgressBar::setValue);


    //启动线程
    thread->start(); Server-receiver.cpp焦点代码

void RecvFile::startListen(unsigned short port)
{
    qDebug()<<port;
    //分配内存空间
    tcpServer = new QTcpServer(this);
    tcpServer->listen(QHostAddress::Any,port);
    connect(tcpServer,&QTcpServer::newConnection,[=]()
    {
      //取出建立好连接的套接字
      tcpSocket = tcpServer->nextPendingConnection();

      isStart = true;
      connect(tcpSocket,&QTcpSocket::readyRead,this,&RecvFile::recvFile);
      connect(tcpSocket,&QTcpSocket::disconnected,this,[=]()
      {
            //断开连接
            tcpSocket->close();
            tcpSocket->deleteLater();
      });
    });
}



void RecvFile:: recvFile()
{
    qDebug()<<"触发recvFile";
    //取出接收的内容
    QByteArray buf = tcpSocket->readAll();
    if(true == isStart)//之前未接收文件的头部信息
    {
      //接收头部
      qDebug()<<"接收头部===";
      isStart = false;
      //解析头部buf的内容格式 <文件名>##<文件大小>
      QString token=QString(buf).section("##",0,0);//token
      qDebug()<<"token1="<<token;
      recvfilesize = 0;//初始化已接收的文件长度为0

      if(token=="197%*FILE*%197"){//是文件
            qDebug()<<"文件";
            filename = QString(buf).section("##",1,1);//文件名
            filesize = QString(buf).section("##",2,2).toUInt();//文件大小
            targetDir=QString(buf).section("##",3,3);//文件应存储的目录
            qDebug()<<"filename="<<filename;
            qDebug()<<"filesize="<<filesize;
            qDebug()<<"targetDir="<<targetDir;
      }
      else if(token=="197%*DOWNLOAD*%197"){//请求下载文件信号
            qDebug()<<"下载文件请求";
            filePath = QString(buf).section("##",1,1);//文件完整路径
            storagePath = QString(buf).section("##",2,2);//文件完整路径
            qDebug()<<"filePath="<<filePath;
            qDebug()<<"storagePath="<<storagePath;
            isStart = true;//信号只是个头,不应该继续接收尾部
            sendDownloadFile(filePath,storagePath);
            return;
      }
      else if(token=="197%*CLIENT_RECEIVED*%197"){
            qDebug()<<"客户端已成功接收文件";
            isStart = true;//信号只是个头,不应该继续接收尾部
            return;
      }
      else{
            qDebug()<<"无效token!";
            return;
      }


      filename=targetDir+"\\"+filename;//设置文件位置
      file.setFileName(filename);
      qDebug()<<"filename="<<filename;
      bool isOk = file.open(QIODevice::WriteOnly);
      if(false == isOk)//打开失败
      {
            return;
      }
    }
    else//文件的头部信息已经接收
    {
      qDebug()<<"接收尾部===";
      qint64 len = file.write(buf);//将buf中的数据写入file文件,并返回实际写入的字节数。
      if(len>0)
      {
            recvfilesize += len;//累计接收大小
            int percent = ((recvfilesize)*100)/filesize;
            //发出更新进度条的信号
            //qDebug()<<"percent="<<percent;
            emit curPercent(percent);
      }

      if(recvfilesize == filesize)
      {
            //emit recvOver();//接收完毕
            qDebug()<<"接收完毕1";
            file.close();
            isStart = true;

            //通知客户端 接收完毕
            // 文件传输成功后发送文件接收完毕的信息给客户端
            //============================================Token==================================================================
            QString token="197%*SERVER_RECEIVED*%197";
            //============================================Token==================================================================
            QByteArray completionMessage = token.toUtf8();
            completionMessage.append("##");

            tcpSocket->write(completionMessage);
            tcpSocket->waitForBytesWritten(); // 等待数据写入完毕
      }
      else{
            qDebug()<<"recvfilesize != filesize";
            //isStart = true;
      }
    }
}

void RecvFile::sendDownloadFile(QString path,QString targetDir)//发送头
{
    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,[=]()
    {
      //关闭定时器
      timer->stop();
      //发送文件数据
      sendDownloadFileData();
    });

    file.setFileName(path);
    //获取文件信息
    QFileInfo info(path);
    filesize = info.size();//获取文件大小
    filename = info.fileName();//获取文件名
    //============================================Token==================================================================
    QString token="197%*FILE*%197";
    //============================================Token==================================================================
    //只读方式打开文件
    bool isOk = file.open(QIODevice::ReadOnly);
    if(true == isOk)
    {
      //创建文件头信息
      QString head = QString("%1##%2##%3##%4").arg(token).arg(filename).arg(filesize).arg(targetDir);
      //创建文件头信息,格式为"文件名##文件大小",例如 "example.txt##1024"
      //发送头部信息
      qint64 len = tcpSocket->write(head.toUtf8());
      if(len>0)//发送成功
      {
            //防止tcp黏包问题,延时20ms
            timer->start(20);
      }
      else
      {
            //发送失败
            file.close();
      }
    }
    else
    {
      QMessageBox::warning(nullptr,"错位","打开文件失败!");
      return;
    }
}




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Qt:多线程TCP服务器客户端双向文件互传,Server端和Client端分离(带进度