Qt实现TFTP Server和 TFTP Client(四)

3.3 Server

Server包括下面3个类:

  • ServerSocket
  • TFtpServer
  • TFtpServerWidget
3.3.1 ServerSocket

ServerSocket从BaseUdp派生实现write接口.

3.3.1.1 ServerSocket定义
class QUdpSocket;
class ServerSocket : public BaseUdp
{
public:
    ServerSocket(QUdpSocket* socket, QHostAddress const& host,
              uint16_t port)
        : socket_(socket)
        , host_(host)
        , port_(port)
    {}

    uint32_t write(const char* data, size_t size) override;
private:
    QUdpSocket* socket_;
    QHostAddress host_;
    uint16_t port_;
};

成员函数说明:

  • write 重载函数,实现父类BaseUdp中定义的write接口。
3.3.1.2 ServerSocket实现
uint32_t ServerSocket::write(const char* data, size_t size)
{
    return socket_->writeDatagram(data, size, host_, port_);
}

函数实现说明:

  • 直接调用QUdpSocket对象的writeDatagram接口。
3.3.2 TFtpServer

TFtpServer类通过TFtpServerFile类实现一个TFTP服务端,接受上下载文件请求。

3.3.2.1 TFtpServer定义
class QUdpSocket;
class TFtpServer:  public QObject
{
Q_OBJECT
public:
    TFtpServer(QObject *parent = nullptr);

    void setFilePath(QString const& filePath);
    void start();
    void stop();
signals:
    void bindError();
    void startFile(QString const&transferId, QString const& fileName);
    void progress(QString const&transferId, quint64 bytes, quint64 total);
    void statusText(QString const& text);
    void stopFile(QString const&transferId);

private slots:
    void readPendingDatagrams();
private:

private:
    QUdpSocket* socket;
    QString filePath_;
    TFtpFileManager::Ptr fileManager_;
    const uint16_t TFTP_PORT = 69;
};

成员函数说明:

  • setFilePath 配置TFTP服务器的下载文件路径.
  • start 启动TFTP服务.
  • stop 停止TFTP服务.

信号说明:

  • bindError 绑定UDP端口错误信号
  • startFile 文件开始传输信号
  • progress 文件传输进度信号
  • statusText 状态文本变化信号
  • stopFile 停止文件传输信号
    槽函数说明:
  • readPendingDatagrams 从TFTP客户端读数据处理函数
3.3.2.1 TFtpServer实现
  • 构造函数
TFtpServer::TFtpServer(QObject *parent)
    : QObject(parent)
    , socket(new QUdpSocket(this))
    , fileManager_(new TFtpFileManager())
{
    connect(socket, &QUdpSocket::readyRead,
            this, &TFtpServer::readPendingDatagrams);
}

函数说明:

  • 创建QUdpSocket对象socket

  • 连接socket的信号和对应槽函数。

  • setFilePath/start/stop/readPendingDatagrams

void TFtpServer::setFilePath(QString const& filePath)
{
    if(!filePath.endsWith("/"))
        filePath_ = filePath + "/";
}

void TFtpServer::start()
{
    if(!socket->bind(TFTP_PORT))
        emit bindError();
}

void TFtpServer::stop()
{
    socket->close();
}

void TFtpServer::readPendingDatagrams()
{
    while (socket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = socket->receiveDatagram();
        QByteArray d = datagram.data();
        QString  transferId = QString("%1:%2").arg(datagram.senderAddress().toString())
                .arg(datagram.senderPort());
        TFtpServerFile::Ptr file = fileManager_->find(transferId.toStdString());
        if(file)
            file->process((uint8_t *)d.data(), d.size());
        else
        {
            ServerSocket* udp = new ServerSocket(socket, datagram.senderAddress(), datagram.senderPort());
            file = TFtpServerFile::Ptr(new TFtpServerFile(udp, filePath_.toStdString(), transferId.toStdString()));
            fileManager_->add(file);
            file->process((uint8_t *)d.data(), d.size());
            emit startFile(transferId, QString::fromStdString(file->filename()));
        }
        if(!file->is_finished())
        {
            if(file->type() == TFtpServerFile::Read)
                emit statusText(QString("Downloding file: %1, progress: %4% blockNumber(%2/%3)")
                                .arg(QString::fromStdString(file->filename()))
                                .arg(file->block_number())
                                .arg(file->block_numbers())
                                .arg(file->block_number() * 100 / file->block_numbers()));
            else
                emit statusText(QString("Uploading file: %1, blockNumber(%2)")
                                .arg(QString::fromStdString(file->filename()))
                                .arg(file->block_number()));
            emit progress(transferId, file->file_bytes(), file->filesize());
        }
        else
        {
            if(file->is_error())
                emit statusText(QString("%1:%2").arg((int)file->error()).arg(QString::fromStdString(file->error_msg())));
            else
                emit statusText(QString());
            emit progress(transferId, file->file_bytes(), file->filesize());
            emit stopFile(transferId);
            fileManager_->remove(file->transfer_id());
        }

    }
}

函数说明:

  • setFilePath 保存文件路径地址
  • start 绑定TFTP端口,启动服务,绑定失败发送bindError信号。
  • stop 关闭socket,停止服务
  • readPendingDatagrams 从socket读取数据包,构造transferId,根据transferId判断是新连接还是旧连接,旧连接则找到对应TFtpServerFile对象处理文件上下载;新连接则TFtpServerFile对象处理文件上下载,并保存TFtpServerFile对象;处理数据完毕后,根据结束与否进行相应的处理。
3.3.3 TFtpServerWidget

TFtpServerWidget从QWidget派生一个窗口类,负责设置下载文件路径,并显示文件传输进度等界面操作。

3.3.3.1 TFtpServerWidget定义
class TFtpServer;
class TFtpServerWidget : public QWidget
{
    Q_OBJECT

public:
    TFtpServerWidget(QWidget *parent = nullptr);
    ~TFtpServerWidget();

private slots:
    void selectTFtpDir();
    void setCurrentDir(QString const& path);
    void onBindError();
    void onStartFile(QString const&transferId, QString const& fileName);
    void onProgress(QString const&transferId, quint64 bytes, quint64 total);
    void onStopFile(QString const&transferId);
private:
    void saveSettinggs();
    void loadSettings();
private:
    Ui::TFtpServerWidget *ui;
    TFtpServer* tftpServer;
    int MAX_PATH_SIZE = 5;

};
3.3.3.2 TFtpServerWidget实现
TFtpServerWidget::TFtpServerWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::TFtpServerWidget)
    , tftpServer(new TFtpServer(this))
{
    ui->setupUi(this);
    loadSettings();

    connect(ui->btnBrowse, SIGNAL(clicked()), this, SLOT(selectTFtpDir()));
    connect(ui->currentDir, SIGNAL(currentIndexChanged(QString)), this, SLOT(setCurrentDir(QString)));
    connect(tftpServer, SIGNAL(startFile(QString,QString)), this, SLOT(onStartFile(QString,QString)));
    connect(tftpServer, SIGNAL(progress(QString,quint64,quint64)), this, SLOT(onProgress(QString,quint64,quint64)));
    connect(tftpServer, SIGNAL(stopFile(QString)), this, SLOT(onStopFile(QString)));
    connect(tftpServer, SIGNAL(bindError()), this, SLOT(onBindError()));
    tftpServer->start();
}

TFtpServerWidget::~TFtpServerWidget()
{
    saveSettinggs();
    tftpServer->stop();
    delete ui;
}

void TFtpServerWidget::selectTFtpDir()
{
    QString filePath = QFileDialog::getExistingDirectory(this,
                        "Select Dir", ui->currentDir->currentText());
    if(filePath.isEmpty())
        return;
    int index  = ui->currentDir->findText(filePath);
    if(index != -1)
        ui->currentDir->setCurrentIndex(index);
    else
    {
        if(ui->currentDir->count() >= MAX_PATH_SIZE)
            ui->currentDir->removeItem(0);
        ui->currentDir->addItem(filePath);
        ui->currentDir->setCurrentIndex(ui->currentDir->count()  - 1);
    }
}

void TFtpServerWidget::setCurrentDir(QString const& path)
{
    tftpServer->setFilePath(path);
}

void TFtpServerWidget::onStartFile(QString const&transferId, QString const& fileName)
{
    ui->clientTables->addTopLevelItem(new QTreeWidgetItem(QStringList()
                                            << transferId << fileName << QTime::currentTime().toString("hh:mm:ss")));
}

void TFtpServerWidget::onProgress(QString const&transferId, quint64 bytes, quint64 total)
{
    QList<QTreeWidgetItem*> items = ui->clientTables->findItems(transferId, Qt::MatchCaseSensitive);
    for(int i = 0; i < items.size(); i++)
    {
        if(total == 0)
            items[i]->setText(5, QString::number(bytes));
        else
        {   items[i]->setText(3, QString("%1%").arg(bytes * 100 / total));
            items[i]->setText(5, QString::number(total));
        }
        items[i]->setText(4, QString::number(bytes));
    }
}

void TFtpServerWidget::onStopFile(QString const&transferId)
{
    QList<QTreeWidgetItem*> items = ui->clientTables->findItems(transferId, Qt::MatchCaseSensitive);
    for(int i = 0; i < items.size(); i++)
    {
        int index = ui->clientTables->indexOfTopLevelItem(items[i]);
        ui->clientTables->takeTopLevelItem(index);
    }
}

void TFtpServerWidget::saveSettinggs()
{
    QSettings settings(QCoreApplication::applicationName(), QCoreApplication::applicationVersion());
    QStringList dirs;
    for(int i = 0; i < ui->currentDir->count(); i++)
        dirs << ui->currentDir->itemText(i);
    settings.setValue("dirs", dirs);
    settings.setValue("currentDir", ui->currentDir->currentText());
}

void TFtpServerWidget::loadSettings()
{
    QSettings settings(QCoreApplication::applicationName(), QCoreApplication::applicationVersion());
    QStringList dirs = settings.value("dirs", QStringList()).toStringList();
    QString currentDir = settings.value("currentDir", QString()).toString();
    ui->currentDir->addItems(dirs);

    int index  = ui->currentDir->findText(currentDir);
    if(index != -1)
    {
        tftpServer->setFilePath(currentDir);
        ui->currentDir->setCurrentIndex(index);
    }
    else
    {
        tftpServer->setFilePath(QApplication::applicationDirPath());
        ui->currentDir->addItem(QApplication::applicationDirPath());
    }
}

void TFtpServerWidget::onBindError()
{
    QMessageBox::critical(this, "TFtpServer", "Port(69) is already occupied!");
    ui->btnBrowse->setDisabled(true);
    ui->currentDir->setDisabled(true);
    setWindowTitle("TFtpServer is not running");
}

Qt实现TFTP Server和 TFTP Client(三)

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flysnow010

你的鼓励就是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值