QT使用QTcpSocket实现简单的FTP文件下载

2019-10-12  本文已影响0人  wuguandong

话不多说,直接上代码

widget.h文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUrl>
#include <QTcpSocket>
#include <QRegExp>
#include <QFile>
#include <QStandardPaths>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    //开始下载按钮槽函数
    void on_btnStart_clicked();
    //commandSocket的readyRead槽函数
    void commandReadyReadSlot();
    //dataSocket的readyRead槽函数
    void dataReadyReadSlot();

    void on_btnClear_clicked();

private:
    //发送用户名
    void sendUser();
    //发送密码
    void sendPass();
    //发送PWD
    void sendPwd();
    //获取当前目录
    void getCurrentDirectory();
    //发送TYPE
    void sendType();
    //发送SIZE
    void sendSize();
    //获取文件大小
    void getFileSize();
    //发送PASV
    void sendPasv();
    //建立dataSocket连接
    void buildDataConnection();
    //发送RETR
    void sendRetr();
    //等待传输完成
    void waitTransferComplete();
    //发送QUIT
    void sendQuit();
    //断开命令连接
    void closeCommandConnection();

private:
    Ui::Widget *ui;

    QUrl url;
    QTcpSocket *commandSocket;
    QTcpSocket *dataSocket;
    QByteArray commandBuffer;
    QByteArray dataBuffer;
    QString expectedReply;
    void (Widget::*nextAction)();
    QString currentDirectory;
    qint64 fileSize;
    qint64 receivedSize;
    QFile file;
    bool hasCreateFile;

};

#endif // WIDGET_H

widget.cpp文件

#pragma execution_character_set("utf-8")
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);

    this->commandSocket = new QTcpSocket(this);
    this->dataSocket = new QTcpSocket(this);
    connect(this->commandSocket, &QTcpSocket::readyRead, this, &Widget::commandReadyReadSlot);
    connect(this->dataSocket, &QTcpSocket::readyRead, this, &Widget::dataReadyReadSlot);
}

Widget::~Widget()
{
    delete ui;
}

//开始下载按钮槽函数
void Widget::on_btnStart_clicked()
{
    url.setUrl(ui->leUrl->text());

    commandSocket->connectToHost(url.host(), url.port()==-1? 21: static_cast<quint16>(url.port()));
    this->expectedReply = "220";
    if(url.userName().isEmpty()){
        url.setUserName("anonymous");
        url.setPassword("chrome@example.com");
    }
    this->nextAction = &Widget::sendUser;

    //初始化成员变量
    this->hasCreateFile = false;
    this->receivedSize = 0;

    ui->progressBar->setValue(0);
}

//commandSocket的readyRead槽函数
void Widget::commandReadyReadSlot()
{
    this->commandBuffer.clear();
    this->commandBuffer = commandSocket->readAll();
    ui->textEdit->append("收到服务器回复:" + this->commandBuffer);

    if(!this->expectedReply.isEmpty() && this->commandBuffer.mid(0,3) == this->expectedReply.toLatin1()){
        if(nextAction) (this->*nextAction)();
    }
    else{
        ui->textEdit->append( QString("错误:收到不符合期待(%1)的回应:(%2)").arg(this->expectedReply).arg(QString("").append(this->commandBuffer)) );
    }
}

//dataSocket的readyRead槽函数
void Widget::dataReadyReadSlot()
{
    this->dataBuffer.clear();
    this->dataBuffer = dataSocket->readAll();

    //写入文件
    qint64 size = file.write(this->dataBuffer);
    this->receivedSize += size;

    //更新进度条
    ui->progressBar->setValue(static_cast<int>(receivedSize / 1024));

    if(this->receivedSize >= this->fileSize){
        dataSocket->disconnectFromHost();
        dataSocket->close();
        file.close();
    }
}

//发送用户名
void Widget::sendUser()
{
    QString command = QString("USER %1\r\n").arg(url.userName());
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "331";
    this->nextAction = &Widget::sendPass;
}

//发送密码
void Widget::sendPass()
{
    QString command = QString("PASS %1\r\n").arg(url.password());
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "230";
    this->nextAction = &Widget::sendPwd;
}

//发送PWD
void Widget::sendPwd()
{
    QString command = "PWD\r\n";
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "257";
    this->nextAction = &Widget::getCurrentDirectory;
}

//获取当前目录
void Widget::getCurrentDirectory()
{
    QRegExp rx(R"(".+")");
    rx.indexIn(this->commandBuffer);
    QString tmp = rx.capturedTexts().at(0);
    this->currentDirectory = tmp.mid(1,tmp.length()-2);

    this->sendType();
}

//发送TYPE
void Widget::sendType()
{
    QString command = "TYPE I\r\n";
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "200";
    this->nextAction = &Widget::sendSize;
}

//发送SIZE
void Widget::sendSize()
{
    QString command = QString("SIZE %1%2\r\n").arg(this->currentDirectory == "/"? "": this->currentDirectory).arg(url.path());
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "213";
    this->nextAction = &Widget::getFileSize;
}

//获取文件大小
void Widget::getFileSize()
{
    this->fileSize = commandBuffer.mid(4, commandBuffer.length()-6).toInt();

    //初始化进度条
    ui->progressBar->setMaximum(static_cast<int>(this->fileSize / 1024));

    this->sendPasv();
}

//发送PASV
void Widget::sendPasv()
{
    QString command = "PASV\r\n";
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "227";
    this->nextAction = &Widget::buildDataConnection;
}

//建立dataSocket连接
void Widget::buildDataConnection()
{
    QRegExp rx(R"(\(\d{1,3},\d{1,3},\d{1,3},\d{1,3},\d{1,3},\d{1,3}\))");
    rx.indexIn(this->commandBuffer);
    QString tmp = rx.capturedTexts().at(0);
    QStringList list = tmp.mid(1,tmp.length()-2).split(',');

    quint16 port = static_cast<quint16>(list.at(4).toInt() * 256 + list.at(5).toInt());
    ui->textEdit->append("**建立数据连接**");
    dataSocket->connectToHost(url.host(), port);
    connect(dataSocket, &QTcpSocket::connected, [=]{
        if(!this->hasCreateFile){
            this->hasCreateFile = true;
            //创建文件
            ui->textEdit->append("**触发了connected信号**");
            file.setFileName(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/").append(url.fileName()));
            bool isOk = file.open(QIODevice::WriteOnly);
            if(!isOk){
                ui->textEdit->append("错误:文件打开失败");
                return;
            }

            this->sendRetr();
        }
    });
}

//发送RETR
void Widget::sendRetr()
{
    QString command = QString("RETR %1%2\r\n").arg(this->currentDirectory).arg(url.path());
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "150";
    this->nextAction = &Widget::waitTransferComplete;
}

//等待传输完成
void Widget::waitTransferComplete()
{
    this->expectedReply = "226";
    this->nextAction = &Widget::sendQuit;
}

//发送QUIT
void Widget::sendQuit()
{
    QString command = "QUIT\r\n";
    ui->textEdit->append("向服务器发送: " + command);
    commandSocket->write(command.toLatin1());
    this->expectedReply = "221";
    this->nextAction = &Widget::closeCommandConnection;
}

//断开命令连接
void Widget::closeCommandConnection()
{
    commandSocket->disconnectFromHost();
    commandSocket->close();
}



void Widget::on_btnClear_clicked()
{
    ui->textEdit->clear();
}
上一篇 下一篇

猜你喜欢

热点阅读