目录
3.2 Client
Client包括下面3个类:
- ClientSocke
- TFtpClient
- TFtpClientWidget
3.2.1 ClientSocke
ClientSocke从BaseUdp派生实现write接口.
3.2.1.1 ClientSocke定义
#include "baseudp.h"
class QUdpSocket;
class ClientSocket : public BaseUdp
{
public:
ClientSocket(QUdpSocket* socket)
: socket_(socket)
{}
uint32_t write(const char* data, size_t size) override;
private:
QUdpSocket* socket_;
};
成员函数说明:
- write 重载函数,实现父类BaseUdp中定义的write接口。
3.2.1.2 ClientSocke实现
#include "clientsocket.h"
#include <QUdpSocket>
uint32_t ClientSocket::write(const char* data, size_t size)
{
return socket_->write(data, size);
}
函数实现说明:
- 直接调用QUdpSocket对象的write接口。
3.2.2 TFtpClient
TFtpClient类通过TFtpClientFile类实现一个TFTP客户端,实现上下载文件。
3.2.2.1 TFtpClient定义
class QUdpSocket;
class TFtpClient : public QObject
{
Q_OBJECT
public:
explicit TFtpClient(QObject *parent = nullptr);
void setHostPort(QString const& host, quint16 port);
void getFile(QString const& localFileName, QString const& remoteFileName);
void putFile(QString const& localFileName, QString const& remoteFileName);
bool isPut() const { return isPut_; }
public slots:
void stopFileTransfer();
signals:
void started();
void progress(quint64 bytes, quint64 total);
void error(QString const& text);
void finished();
private slots:
void connected();
void connectError(QAbstractSocket::SocketError error);
void readPendingDatagrams();
private:
QUdpSocket* socket;
QString host_;
quint16 port_;
QString localFileName_;
QString remoteFileName_;
TFtpClientFile::Ptr tftpFile_;
bool isPut_ = false;
};
成员函数说明:
- setHostPort 配置TFTP服务器的IP地址和端口.
- getFile 从TFTP服务器下载文件.
- putFile 向TFTP服务器上传文件.
- isPut 是否是上传
- stopFileTransfer 终止文件传输
信号说明:
- started 文件传输开始信号
- progress 传输进度信号
- error 错误信号
- finished 传输完成信号
槽函数说明:
- connected 与TFTP服用器连接成功后的处理函数
- connectError 与TFTP服用器连接失败后的处理函数
- readPendingDatagrams 从TFTP服用器读数处理函数
3.2.2.2 TFtpClient实现
- 构造函数
TFtpClient::TFtpClient(QObject *parent)
: QObject(parent)
, socket(new QUdpSocket(this))
{
connect(socket, &QUdpSocket::readyRead,
this, &TFtpClient::readPendingDatagrams);
connect(socket, &QUdpSocket::connected,
this, &TFtpClient::connected);
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(connectError(QAbstractSocket::SocketError)));
}
函数说明:
-
创建QUdpSocket对象socket
-
连接socket的信号和对应槽函数。
-
setHostPort/getFile/putFile
void TFtpClient::setHostPort(QString const& host, quint16 port)
{
host_ = host;
port_ = port;
}
void TFtpClient::getFile(QString const& localFileName, QString const& remoteFileName)
{
isPut_ = false;
localFileName_ = localFileName;
remoteFileName_ = remoteFileName;
socket->connectToHost(host_, port_);
}
void TFtpClient::putFile(QString const& localFileName, QString const& remoteFileName)
{
isPut_ = true;
localFileName_ = localFileName;
remoteFileName_ = remoteFileName;
socket->connectToHost(host_, port_);
}
函数说明:
-
setHostPort 保存主机地址和端接口
-
getFile 保存本地文件名和远程文件名,设置为下载,连接TFTP服务器
-
putFile 保存本地文件名和远程文件名,设置为上传,连接TFTP服务器
-
stopFileTransfer/connected/connectError/readPendingDatagrams
void TFtpClient::stopFileTransfer()
{
socket->disconnectFromHost();
}
void TFtpClient::connected()
{
ClientSocket* udp = new ClientSocket(socket);
tftpFile_ = TFtpClientFile::Ptr(new TFtpClientFile(udp));
bool isOK = true;
if(isPut_)
isOK = tftpFile_->putFile(localFileName_.toStdString(),
remoteFileName_.toStdString(), TFtp::BINARY);
else
isOK = tftpFile_->getFile(localFileName_.toStdString(),
remoteFileName_.toStdString(), TFtp::BINARY);
if(!isOK)
emit error("Local File not Found");
else
emit started();
}
void TFtpClient::connectError(QAbstractSocket::SocketError)
{
emit error("Connect host is failed");
}
void TFtpClient::readPendingDatagrams()
{
while (socket->hasPendingDatagrams()) {
QNetworkDatagram datagram = socket->receiveDatagram();
QByteArray d = datagram.data();
if(tftpFile_)
{
tftpFile_->process((uint8_t *)d.data(), d.size());
emit progress(tftpFile_->file_bytes(), tftpFile_->filesize());
if(tftpFile_->is_finished())
{
if(tftpFile_->is_error())
emit error(QString::fromStdString(tftpFile_->error_msg()));
else
emit finished();
}
}
}
}
函数说明:
- stopFileTransfer 与TFTP服务器断开连接,终止文件传输。
- connected 连接上TFTP服务器后,创建TFtpClientFile对象,开始上传/下载文件
- connectError 与TFTP服务器连接失败,发送连接失败信号量。
- readPendingDatagrams 从TFTP服务器读取数据给TFtpClientFile对象处理,如果传输结束,有错误发送error信号,没错误发送finished信号。
3.2.3 TFtpClientWidget
TFtpClientWidget从QWidget派生一个窗口类,负责发起上下载文件,并显示文件传输进度等界面操作。
3.2.3.1 TFtpClientWidget定义
class TFtpClient;
class TFtpClientWidget : public QWidget
{
Q_OBJECT
public:
TFtpClientWidget(QWidget *parent = nullptr);
~TFtpClientWidget();
private slots:
void onGetFile();
void onPutFile();
void onStarted();
void onProgress(quint64 bytes, quint64 total);
void onStop();
void onFinished();
void onError(QString const& error);
void onSelectLocalFile();
private:
void enableButtons(bool enable);
private:
Ui::TFtpClientWidget *ui;
TFtpClient* tftpClient;
};
3.2.3.2 TFtpClientWidget实现
TFtpClientWidget::TFtpClientWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::TFtpClientWidget)
, tftpClient(new TFtpClient(this))
{
ui->setupUi(this);
connect(ui->btnBrowse, SIGNAL(clicked()), this, SLOT(onSelectLocalFile()));
connect(ui->btnGet, SIGNAL(clicked()), this, SLOT(onGetFile()));
connect(ui->btnPut, SIGNAL(clicked()), this, SLOT(onPutFile()));
connect(ui->btnBreak, SIGNAL(clicked()), this, SLOT(onStop()));
connect(tftpClient, &TFtpClient::started, this, &TFtpClientWidget::onStarted);
connect(tftpClient, &TFtpClient::progress, this, &TFtpClientWidget::onProgress);
connect(tftpClient, &TFtpClient::finished, this, &TFtpClientWidget::onFinished);
connect(tftpClient, &TFtpClient::error, this, &TFtpClientWidget::onError);
}
TFtpClientWidget::~TFtpClientWidget()
{
delete ui;
}
void TFtpClientWidget::onGetFile()
{
tftpClient->stopFileTransfer();
tftpClient->setHostPort(ui->lineEditHost->text(), ui->spinBoxPort->value());
tftpClient->getFile(ui->lineEditLocalFile->text(), ui->lineEditRemoteFile->text());
ui->progressBar->setValue(0);
}
void TFtpClientWidget::onPutFile()
{
tftpClient->stopFileTransfer();
tftpClient->setHostPort(ui->lineEditHost->text(), ui->spinBoxPort->value());
tftpClient->putFile(ui->lineEditLocalFile->text(), ui->lineEditRemoteFile->text());
ui->progressBar->setValue(0);
}
void TFtpClientWidget::onStarted()
{
enableButtons(false);
}
void TFtpClientWidget::onProgress(quint64 bytes, quint64 total)
{
if(total > 0)
ui->progressBar->setValue(bytes * 100 / total);
else
{
int value = ui->progressBar->value();
ui->progressBar->setValue(QRandomGenerator(value).bounded(value, 99));
}
}
void TFtpClientWidget::onStop()
{
enableButtons(true);
tftpClient->stopFileTransfer();
}
void TFtpClientWidget::onFinished()
{
if(tftpClient->isPut())
QMessageBox::information(this, "TFtpClient", "Put is done!");
else
QMessageBox::information(this, "TFtpClient", "Get is done!");
enableButtons(true);
}
void TFtpClientWidget::onError(QString const& error)
{
QMessageBox::critical(this, "TFtpClient", error);
enableButtons(true);
}
void TFtpClientWidget::onSelectLocalFile()
{
static QString filePath;
QFileDialog dialog(this, tr("Select File"), filePath, tr("All files (*.*)"));
if(dialog.exec() == QDialog::Rejected)
return;
QStringList fileNames = dialog.selectedFiles();
if(fileNames.isEmpty())
return;
QString fileName = fileNames.first();
filePath = QFileInfo(fileName).filePath();
ui->lineEditLocalFile->setText(fileName);
}
void TFtpClientWidget::enableButtons(bool enable)
{
ui->btnGet->setEnabled(enable);
ui->btnPut->setEnabled(enable);
ui->btnBreak->setDisabled(enable);
}
3.2.4 PutFile序列图
3.2.4 GetFile序列图
Qt实现TFTP Server和 TFTP Client(二) Qt实现TFTP Server和 TFTP Client(四)