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();
}