一、服务端,选择文件 发送文件

1、服务器端,使用UI设计师编辑好界面

android tcp文件上传 tcp传输文件_文件大小

 

 2、创建成员对象

QTcpServer *tcpserver; // 监听·套接字
    QTcpSocket *tcpsocket; // 通信套接字
    QFile file;        // 选择文件对象
    QString fileName;  // 文件名字
    qint64 fileSize;   // 文件大小
    qint64 SendSize;   // 已发送文件的大小
    void SendData();   // 发送文件数据
    QTimer timer;      // 定时器对象

3、与客户端建立连接 并处理客户端的反馈信息

setWindowTitle("服务器端口8888");
    tcpserver = new QTcpServer(this);
    // 初始设置 两个按钮都不允许按下
    ui->buttonSend->setEnabled(false);
    ui->buttonSelect->setEnabled(false);
    // 监听网口绑定的任意IP 端口位8888
    tcpserver->listen(QHostAddress::Any,8888);
    // 处理新的连接请求
    connect(tcpserver,&QTcpServer::newConnection,
            [=]()
            {
                // 获取最近的套接字
                tcpsocket = tcpserver->nextPendingConnection();
                QString ip = tcpsocket->peerAddress().toString();  // 获取套接字的IP
                quint16 port = tcpsocket->peerPort();  // 获取套接字的端口
                QString str = QString("[%1:%2]:连接成功").arg(ip).arg(port);  // 组包
                ui->textEdit->setText(str);

                // 成功连接后可以按按钮
                ui->buttonSelect->setEnabled(true);

                // 处理客户端发送来的信息
                connect(tcpsocket,&QTcpSocket::readyRead,
                        [=]()
                        {
                           QByteArray arr = tcpsocket->readAll();
                           if(QString(arr) == "file done")
                           {
                               // 文件接收完成
                               ui->textEdit->append("文件发送完成");
                               file.close();

                               // 断开客户端
                               tcpsocket->disconnectFromHost();
                               tcpsocket->close();
                           }
                        });

            });
    // 定时器发送文件信息
    connect(&timer,&QTimer::timeout,
            [=]()
            {
                timer.stop();  // 停止定时器
                SendData();  // 发送头信息
            });

4、与客户端建立连接后,服务端选择需要发送的文件

// 选择需要发送的文件
void ServerWidget::on_buttonSelect_clicked()
{
    QString filepath = QFileDialog::getOpenFileName(this,"open","../");
    if(false == filepath.isEmpty())
    {
        // 获取文件信息
        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(false == isok)
        {
            qDebug()<< "只读方式打开文件失败";
        }else {
            ui->textEdit->append(filepath);
            ui->buttonSelect->setEnabled(false);
            ui->buttonSend->setEnabled(true);
        }

    }else {
        qDebug() <<"选择文件路径出错";
    }
}

5、发送文件,需要先发送文件的信息

// 发送TCP头  发送文件名 文件大小信息
void ServerWidget::on_buttonSend_clicked()
{
    // 先发送文件头信息
    QString head = QString("%1##%2").arg(fileName).arg(fileSize);
    qint64 len = tcpsocket->write(head.toUtf8());
    if(len > 0){
        // 头部信息发送成功
        // 防止沾包 启动定时器 需要延时20ms
        timer.start(20);  
    }else {
        qDebug() << "头部信息发送失败";
        file.close();
        ui->buttonSelect->setEnabled(true);
        ui->buttonSend->setEnabled(false);
    }
}

6、发送文件

// 发送文件
void ServerWidget::SendData()
{
    qint64 len=0;
    do{
        char buf[4*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->append("文件发送完毕");
        ui->buttonSend->setEnabled(false);
        file.close();  // 关闭打开的文件
         
        // 主动与客户端断开
        tcpsocket->disconnectFromHost();
        tcpsocket->close();
    }
}

 

 

二、处理完服务端,处理客户端

1、客户端使用UI设计师 编辑如下

android tcp文件上传 tcp传输文件_客户端_02

 

 2、创建成员变量

QTcpSocket *tcpsocket;  // 通信套接字
    QFile file;                   // 文件操作 对象
    QString fileName;       // 文件名称
    qint64 fileSize;  // 文件大小
    qint64 recvSize;  // 接收到文件大小
    bool isStart;

3、请求连接至服务端

// 请求与服务端连接
void ClientWidget::on_buttonConnect_clicked()
{
    QString ip = ui->lineEditIP->text();  // 获取IP
    quint16 port = ui->lineEditPort->text().toInt();  // 获取端口
    tcpsocket->connectToHost(QHostAddress(ip),port);  // 连接至服务端
    ui->progressBar->setValue(0);// 重新连接时 进度条复位
}

4、处理头部信息 以及接受数据处理

setWindowTitle("客户端");
    ui->progressBar->setValue(0);  // 初始化进度条位置位0

    tcpsocket = new QTcpSocket(this);
    isStart = true;
    // 准备接收数据
    connect(tcpsocket,&QTcpSocket::readyRead,
            [=]()
            {
                // 读取服务端发送来的数据
                QByteArray buf = tcpsocket->readAll();
                QString str = QString(buf);   // 将字节数组转为字符串

                //处理头部信息
                if(true == isStart)
                {
                    isStart = false;
                    // 服务端组包
                    // QString head = QString("%1##%2").arg(fileName).arg(fileSize);
                    // 获取文件信息 拆包
                    fileName = str.section("##",0,0);   // 获取文件名称
                    fileSize = str.section("##",1,1).toInt();  // 文件大小
                    recvSize = 0; // 接收到的文件大小至0

                    // 打开文件
                    file.setFileName(fileName);
                    bool isok = file.open(QIODevice::WriteOnly);
                    if(false == isok)
                    {
                        // 打开文件出错
                    }

                    QString str = QString("接收文件:[%1:%2]").arg(fileName).arg(fileSize/1024);
                    QMessageBox::information(this,"文件信息",str);
                    // 设置进度条  注意进度条数据越界
                    ui->progressBar->setMinimum(0);
                    ui->progressBar->setMaximum(int(fileSize/1024));
                    ui->progressBar->setValue(0);

                }else {  // 数据处理
                    // 保存文件
                    qint64 len = file.write(buf);
                    if(len > 0) // 读取到了数据
                    {
                        recvSize += len;  // 接受到的数据累积
                    }
                    // 设置进度条位置
                    ui->progressBar->setValue(int(recvSize/1024));
                    
                    // 接收数据完成
                    if(recvSize == fileSize)
                    {
                        // 给客户端发提示信息
                        tcpsocket->write("file done");
                         file.close(); // 关闭文件
                         QMessageBox::information(this,"完成","文件接收完成");
                         tcpsocket->disconnectFromHost();
                         tcpsocket->close();
                    }

                }
            });

5、最后结果

android tcp文件上传 tcp传输文件_文件大小_03

 6、最后需要注意一点:

  tcp传输数据,服务端发送数据,可能发送好几次,客户端才可能读一次;,所以 发送数据 与接受数据是不对等的;