• 27Tcp文件传输


    前面介绍了TCP和UDP的通信,只是文体通信,只能传送文字。本次介绍文件传输,也就是文件读写和TCP通信的结合。

    解析:根据之前的TCP通信,建立彼此的连接。服务器选择文件,首先将文件的基本信息发送给客户端。如:文件名,文件大小(用于进度条使用)。如上显示,“head#hello#1024”,即头部信息,hello就是文件名,1024是文件大小。如此客户端可以在某个磁盘创建好文件,便于后面的写入。服务端每次读取MK大小的数据,每读取一次,传输一次,客户端写入一次但要累计发送文件的大小,便于与原文件大小比较。

    黏包问题:
    如果头部信息和文件内容重合或追加。比如”head#hello#1024abdjsjdas”,那么“abdjsjdas”本质是内容信息而不是头部。

    解决方式:设定一个定时器,在发出头部信息后,间隔N毫秒后再发送内容信息。

    服务端

    FileServer.h

    #ifndef FILESERVER_H

    #define FILESERVER_H

    #include <QWidget>

    #include <QTcpServer>

    #include <QTcpSocket>

    #include <QFile>

    #include <QFileDialog>

    #include <QFileInfo>

    #include <QTimer>

    namespace Ui {

    class Widget;

    }

    class Widget : public QWidget

    {

        Q_OBJECT

    public:

        explicit Widget(QWidget *parent = 0);

        void SendData();

        ~Widget();

    private slots:

        void on_pushButton_clicked();       //选择文件

        void on_pushButton_2_clicked();     //发送文件

    private:

        Ui::Widget *ui;

        QTcpServer *m_listenSock;   //监听套接字

        QTcpSocket *m_serverSock;   //通信套接字

        QFile m_file;           //文件

        QString m_strFileName;  //文件名

        qint64 m_fileSize;      //文件大小

        qint64 m_sendSize;      //已发送的大小

        QTimer timer;           //定时器

    };

    #endif // FILESERVER_H

    FileServer.cpp

    #include "FileServer.h"

    #include "ui_widget.h"

    #include <QMessageBox>

    Widget::Widget(QWidget *parent) :

        QWidget(parent),

        ui(new Ui::Widget)

    {

        ui->setupUi(this);

        this->setWindowTitle("服务器端口:888");

        //将选择文件和发送文件的灰度

        m_listenSock=new QTcpServer(this);

        ui->pushButton->setEnabled(false);

        ui->pushButton_2->setEnabled(false);

        //监听

        bool bIsOk=m_listenSock->listen(QHostAddress::Any,888);

        if(bIsOk==true)

        {

            connect(m_listenSock,&QTcpServer::newConnection,

                    [=]()

                    {

                        //获取客户端的通信套接字

                        m_serverSock=m_listenSock->nextPendingConnection();

                        QString IP=m_serverSock->peerAddress().toString();

                        quint16 port=m_serverSock->peerPort();

                        QString strTemp=QString("[%1:%2]接入成功").arg(IP).arg(port);

                        ui->textEdit->setText(strTemp);

                        //激活选择文件按钮

                        ui->pushButton->setEnabled(true);

                    }

            );

        }

        connect(&timer,&QTimer::timeout,

                [=]()

               {

                    //停止定时器,并开始发送文件内容

                    timer.stop();

                    SendData();

                }

        );

    }

    Widget::~Widget()

    {

        delete ui;

        delete m_listenSock;

        m_listenSock=NULL;

        m_serverSock=NULL;

    }

    void Widget::on_pushButton_clicked()

    {

        QString strFilePath=QFileDialog::getOpenFileName(this,"Open","../");

        if(strFilePath.isEmpty()==false)

        {

             //文件基本信息

             QFileInfo info(strFilePath);

             m_strFileName=info.fileName();

             m_fileSize=info.size();

             m_sendSize=0;

             //qDebug()<<m_strFileName<<m_fileSize;

             //指定文件名称

             m_file.setFileName(strFilePath);

             //打开文件

             bool bIsOpen=m_file.open(QIODevice::ReadOnly);

             if(bIsOpen==true)

             {

                ui->textEdit->append(strFilePath);

                //激活发送文件按钮,灰度选择文件按钮

                ui->pushButton->setEnabled(false);

                ui->pushButton_2->setEnabled(true);

             };

        }

        else

        {

            //打开失败

        }

    }

    void Widget::on_pushButton_2_clicked()

    {

        //发送头部信息

        QString strHead=QString("%1##%2")

                .arg(m_strFileName)

                .arg(m_fileSize);

        qint64 len=m_serverSock->write(strHead.toUtf8());

        //发送成功

        if(len>0)

        {

            //防止黏包,头部信息和内容信息混淆

            timer.start(1000);

        }

        else

        {

            //发送失败,重新选择文件

            m_file.close();

            ui->pushButton->setEnabled(true);

            ui->pushButton_2->setEnabled(false);

        }

    }

    void Widget::SendData()

    {

        qint64 len=0;       //每次读取的大小

        do

        {

            //每次读取、发送4K

            char pBuf[4*1024]={0};

            len=0;

            //读多少,发送多少

            len=m_file.read(pBuf,sizeof(pBuf));

            m_serverSock->write(pBuf,len);

            //累加发送了多少

            m_sendSize+=len;

        }while(len>0);

        //len>0,表明有数据读取。0数据为空,<0读取失败。

        if(m_fileSize==m_sendSize)

        {

            //QMessageBox::information(this,"提示","发送完成!");

            ui->textEdit->append("发送完成!");

            //激活选择文件按钮,灰度发送文件按钮

            ui->pushButton->setEnabled(true);

            ui->pushButton_2->setEnabled(false);

            m_file.close();

            //m_serverSock->disconnectFromHost();

            //m_serverSock->close();

        }

    }

    同一程序添加两个窗口,且有设计器。新建一个文件,但不是C++类文件,而是QT且带有界面类的,最后在main中使用show()直接显示。如下图:

    client.h

    #ifndef CLIENT_H

    #define CLIENT_H

    #include <QWidget>

    #include <QTcpSocket>

    #include <QFile>

    #include <QProgressBar>

    namespace Ui {

    class Client;

    }

    class Client : public QWidget

    {

        Q_OBJECT

    public:

        explicit Client(QWidget *parent = 0);

        ~Client();

    private slots:

        void on_pushButton_clicked();       //请求连接

        void on_pushButton_2_clicked();     //关闭连接

    private:

        Ui::Client *ui;

        QTcpSocket *m_clientSock;   //通信套接字

        QProgressBar m_proBar;  //进度条

        QFile m_file;           //文件

        QString m_strFileName;  //文件名

        qint64 m_fileSize;      //文件大小

        qint64 m_recvSize;      //已接收的大小

    };

    #endif // CLIENT_H

    client.cpp

    #include "client.h"

    #include "ui_client.h"

    #include <QMessageBox>

    #include <QFileDialog>

    Client::Client(QWidget *parent) :

        QWidget(parent),

        ui(new Ui::Client)

    {

        ui->setupUi(this);

        this->setWindowTitle("客户端");

        m_clientSock=new QTcpSocket(this);

        //初始化进度条

        ui->progressBar->setValue(0);

        connect(m_clientSock,&QTcpSocket::connected,

                [=]()

                {

                    ui->textEdit->setText("连接成功!");

                }

                );

        //判断文件头部信息还是内容信息

        static bool bIsHead=true;

        //通信

        connect(m_clientSock,&QTcpSocket::readyRead,

                [=]()

                {

                    //获取服务器发来的内容

                    QByteArray byteBuf=m_clientSock->readAll();

                    //取出文件头部信息

                    if(bIsHead)

                    {

                        bIsHead=false;

                        //"filename##9999"

                        //解析头部信息

                        m_strFileName=QString(byteBuf).section("##",0,0);

                        m_fileSize=QString(byteBuf).section("##",1,1).toInt();

                        m_recvSize=0;

                        //根据文件的大小,设置进度条范围

                        ui->progressBar->setRange(0,m_fileSize/1024);

                        //m_strFileName="G:\"+m_strFileName;   //自拟文件路径

                        m_file.setFileName(m_strFileName);      //默认源程序上一级目录

                        bool bIsOpen=m_file.open(QIODevice::WriteOnly);

                        if(bIsOpen==false)

                        {

                            //打开失败

                        }

                    }

                    //将读取的内容写入新文件

                    else

                    {

                        //写入文件

                        qint64 len=m_file.write(byteBuf);

                        m_recvSize+=len;

                        //更新进度条

                        ui->progressBar->setValue(m_recvSize/1024);

                        //接收完毕

                        if(m_recvSize==m_fileSize)

                        {

                            QMessageBox::information(this,"提示","接收完成!");

                            QString strTemp=QString("%1,接收完成").arg(m_strFileName);

                            ui->textEdit->append(strTemp);

                            ui->progressBar->setValue(0);

                            m_file.close();

                            //此处一定改,不然发下一文件失败,无法获取头部信息

                            bIsHead=true;

                        }

                    }

                }

                );

    }

    Client::~Client()

    {

        delete ui;

        delete m_clientSock;

        m_clientSock=NULL;

    }

    void Client::on_pushButton_clicked()

    {

        //设置对方的IP和端口

        QString ip=ui->IP->text();

        quint16 port=ui->Port->text().toInt();

        //请求连接

        m_clientSock->connectToHost(ip.toUtf8(),port);

    }

    void Client::on_pushButton_2_clicked()

    {

        //断开连接

        m_clientSock->disconnectFromHost();

        m_clientSock->close();

    }

    main.cpp

    #include "FileServer.h"

    #include <QApplication>

    #include "client.h"

    int main(int argc, char *argv[])

    {

        QApplication a(argc, argv);

        Widget w;

        w.show();

        Client c;

        c.show();

        return a.exec();

    }

    程序结果图:

     

  • 相关阅读:
    Ozone数据探查服务的增量数据更新机制
    HDFS的块Topology位置重分布
    聊聊更为高效的ACL认证方式
    YARN Federation的架构设计
    Confluence 6 用户宏示例
    Confluence 6 用户宏示例
    Confluence 6 用户宏示例
    Confluence 6 用户宏最佳实践
    Confluence 6 编辑和删除用户宏
    Confluence 6 创建一个用户宏
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/9215808.html
Copyright © 2020-2023  润新知