Qt网络编程之TCP通信(二)文件传输

本文详细介绍Qt环境下TCP文件传输流程,包括服务器与客户端的创建、监听、连接、文件选择与发送,以及客户端的数据接收与处理。通过实例展示了跨平台传输效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、流程图

上一篇博客介绍了Qt下Tcp通信流程,并实现了简单的文本传输,本文主要介绍Tcp文件传输过程。
流程图如下:

  1. 服务器
Created with Raphaël 2.2.0 开始 创建监听套接字对象 和通信套接字对象 监听套接字监听指定的Ip和端口 是否有新的 连接请求? 获取客户端信息 选择文件,并 发送文件信息 是否收 到应答? 发送文件 是否发 送完成? 结束 yes no yes no yes no
  1. 客户端
Created with Raphaël 2.2.0 开始 创建通信套接字对象 连接到指定IP和端口 是否接收 到数据? 是否是第一 帧数据? 发送应答信息 选取保存路径 数据写入文件 文件是否写 入完毕? 结束 yes no yes no yes no

二、项目创建

  1. 服务器
    在这里插入图片描述
    在这里插入图片描述
    1)创建监听套接字和通信套接字:

    tcpServer = new QTcpServer(this);
    tcpSocket = NULL;
    

    2)监听是否有新的连接请求:

     tcpServer->listen(QHostAddress::Any,6666);
     connect(tcpServer,SIGNAL(newConnection()),this,SLOT(HaveNewConnection()));
    

    3)获取客户端的信息:

     void Widget::HaveNewConnection()
     {
         tcpSocket = tcpServer->nextPendingConnection();
         QString ip = tcpSocket->peerAddress().toString();
         quint16 port = tcpSocket->peerPort();
         QString str = QString("[%1:%2] 成功连接").arg(ip).arg(port);
         connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(RcvData()));
         QMessageBox::information(this,"Info",str,QMessageBox::Ok);
     }
    

    4)选择文件

    void Widget::on_pushButton_choose_clicked()
    {
        QString filePath = QFileDialog::getOpenFileName(this,"open","../");
        if(!filePath.isEmpty())
        {
            fileName.clear();
            fileSize = 0;
            sendSize = 0;
            QFileInfo info(filePath);
            fileName = info.fileName();
            fileSize = info.size();
            file.setFileName(filePath);
            if(file.open(QIODevice::ReadOnly))
            {
                ui->textEdit->setText(tr("打开文件 ").append(filePath).append("\n"));
            }
            else
            {
                qDebug()<<"文件打开失败";
            }
        }
        else
        {
            qDebug()<<"文件路径出错";
        }
    }
    

    其中fileSize是文件总的字节数,sendSize是已经发送的字节数,打开文件时二者先清零,当sendSize == fileSize时表明文件已经发送完成。
    5)发送文件信息

    void Widget::on_pushButton_send_clicked()
    {
        QString head = tr("%1##%2").arg(fileName).arg(fileSize);
        quint64 len = tcpSocket->write(head.toUtf8().data());
        if(len<=0)
        {
            qDebug()<<"头部信息发送失败 ";
            file.close();
        }
    }
    

    包括文件名和文件大小,这一帧数据应独立于文件数据之外,换句话说,应该确定客户端收到文件信息之后再发送文件数据。
    6)发送文件数据

    void Widget::SendFile()
    {
        qint64 len = 0;
        do{
            char buf[64*1024] = {0};
            len = 0;
            len = file.read(buf,sizeof(buf));
            len = tcpSocket->write(buf,len);
            sendSize += len;
        }while(len > 0);
        if(sendSize == fileSize)
        {
            ui->textEdit->setText("文件发送完毕 \n");
            file.close();
            tcpSocket->disconnect();
            tcpSocket->close();
        }
    }
    
  2. 客户端
    在这里插入图片描述
    在这里插入图片描述
    1)创建通信套接字,并监听是否接收到数据

    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(RcvFile()));
    

    2)连接到指定服务器IP和端口

    void Client::on_pushButton_connect_clicked()
    {
        QString ip = ui->lineEdit_ip->text();
        quint16 port = ui->lineEdit_port->text().toInt();
        tcpSocket->connectToHost(ip,port);
    }
    

    3)接收数据并处理

    void Client::RcvFile()
    {
        QByteArray buf = tcpSocket->readAll();
        if(isStart)
        {
            isStart = false;
            QString dir = QFileDialog::getExistingDirectory(this,"选择文件夹 ","./"); 
            fileName = dir.append("/").append(QString(buf).section("##",0,0));
            fileSize = QString(buf).section("##",1,1).toInt();
            QString info = tr("文件名称:").append(QString(buf).section("##",0,0)).append("\n")
                      .append("保存路径:").append(dir).append("   \n")
                      .append("文件大小:").append(QString::number(fileSize/1024.0/1024.0,'f',2)).append("Mb");
            rcvSize = 0;
            qDebug()<<fileName<<fileSize;
            ui->textEdit_filemsg->setText(info);
            tcpSocket->write(QString(buf).section("##",0,0).toUtf8().data());
            file.setFileName(fileName);
            if(file.open(QIODevice::WriteOnly))
            {
    
            }
            else
            {
                qDebug()<<"write only error";
            }
    
            mTime.start();
        }
        else
        {
            qint64 tmp = 0;
            static qint64 cnt = 0;
            cnt++;
            qint64 len = file.write(buf);
            rcvSize += len;
            tmp += len;
            int progress = (int)((rcvSize*1.0)/(fileSize*1.0) * 100);
            if(cnt%10 == 0)
            {
                float speed;
                QTime current_time = QTime::currentTime();
                int msec = current_time.msec();
                if(last_time != 0)
                {
                    if(msec<last_time)
                        speed = tmp * 1000.0/((msec + 1000-last_time)*1024.0*1024.0);
                    else if(msec == last_time)
                        speed = 100.0;
                    else
                        speed = tmp * 1000.0/((msec-last_time)*1024.0*1024.0);
                }
                last_time = msec;
                tmp = 0;
                ui->progressBar->setValue(progress);
                if(cnt%100 == 0)
                    ui->label_speed->setText(QString::number(speed,'f',2).append("Mb/s"));
    
            }
            if(rcvSize == fileSize)
            {
                QString str = ("下载时间:");
                ui->textEdit_filemsg->append(str.append(QString::number(mTime.elapsed()/1000.0,'f',1)).append("s"));
                ui->progressBar->setValue(100);
                ui->label_speed->setText("0.00");
                file.close();
                QMessageBox::information(this,"完成","文件接收完成");
                tcpSocket->disconnectFromHost();
                tcpSocket->close();
                isStart = true;
            }
        }
    }
    

    首先判断数据是不是第一帧,如果是,则说明是文件信息,对应应该选择文件存储路径并向服务器发送应答(服务器接收到应答才开始发送文件);如果不是第一帧 则说明是文件数据,对应执行写文件操作,另外还有一些进度和速度显示操作。

    三、实例测试

    由于Qt的跨平台特性,项目中将客户端移植到虚拟机中的linux系统下,服务器留在windows中,测试传输效果如下:在这里插入图片描述
    项目连接:https://gitee.com/Mr-Yslf/BlogResources.git

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值