首页 > 其他分享 >Qt实现TFTP Server和 TFTP Client(四)

Qt实现TFTP Server和 TFTP Client(四)

时间:2024-03-27 10:31:29浏览次数:35  
标签:const Qt currentDir void Client ui QString file TFTP

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

标签:const,Qt,currentDir,void,Client,ui,QString,file,TFTP
From: https://blog.csdn.net/flysnow010/article/details/137069562

相关文章

  • Qt - 音视频播放2
    解码器下载链接:LAVfilters 音视频播放播放内存中的音乐QFileread("./music/Nevada.mp3");  if(!read.open(QIODevice::ReadOnly)) {    qDebug()<<"文件打开失败,请重试~"; }  //下面用的对象,必须动态申请,不然构造函数执行完毕,局部变量内存......
  • VS、Qt编译遇到的错误
    ---1、404  NOTFOUND  downloading'http://mitk.org/download/thirdparty/DCMQI.tar.gz'failed       camke中的ep路径没有配置好---2、C4996'strcpy':Thisfunctionorvariablemaybeunsafe.Considerusingstrcpy_sinstead.Todisabledepre......
  • Linux Mint下Qt Creator无法输入中文解决办法
    ubuntu下有对应的fcitx-frontend-qt6软件包,直接安装就能解决问题。但是linuxmint只有基于qt5的,目前使用Qtonlineinstaller安装的QtCreator是基于Qt6.6编译的所以,只能自己编译对应的fcitx-frontend-qt6动态库,然后放到对应目录下首先下载对应的源码gitclonehttps://github......
  • 【问题处理】cannot register qt5vs vs2010 help
    问题描述:安装qt-vs-addin-1.2.4-opensource时,在安装过程中弹出错误窗口,错误信息为cannotregisterqt5vsvs2010help;安装完成后,打开VS2010无法使用插件。解决方案:Window10搜索cmd并使用管理员身份运行,随后输入如下命令"C:\ProgramFiles(x86)\MicrosoftSDKs\Windows\v7......
  • qt_Opencv (学习笔记) - 隐身术
    我们前面一起学习了Opencv库中的一些函数并且做了一个小练习,想必大家对Opencv库有了一定的了解。接下来让我来带着大家来完成今天的小项目吧!有了前面几个文章的基础,我们接下来来实现“隐身术”就比较简单了。先让我来展示一下隐身术的效果吧!我们想要实习隐身术,首先我们......
  • Qt QTcpSocket 对连接服务器中断的不同情况进行判定
    简述对于一个C/S结构的程序,客户端有些时候需要实时得知与服务器的连接状态。而对于客户端与服务器断开连接的因素很多,现在就目前遇到的情况进行一下总结。分为下面六种不同情况   客户端网线断开   客户端网络断开   客户端通过HTTP代理连接服务器,代理机器断开代......
  • How to get the client IP address with Javascript only
    LearnhowtogettheclientIPaddress(localandprivate)usingonlyjavascript.​​Javascript无法获取(也无法存储)客户端IP,但是Javascript能够创建Http请求,并且服务器端语言能够检索用户的公共IP,因此您可以利用这一优势。换句话说,如果你想检索用户的公共IP,你将依赖于对任......
  • How to get the client IP address with Javascript only
    LearnhowtogettheclientIPaddress(localandprivate)usingonlyjavascript.​​Javascriptisunabletoget(norstoressomewhere)theclientIP,howeverjavascriptisabletocreateHttprequests,andserversidelanguagesareabletoretrievetheu......
  • Qt 检查int某一位数据是否为1
    1#include<QCoreApplication>2#include<QString>34intmain(intargc,char*argv[])5{6QCoreApplicationa(argc,argv);78intnumber=123;//例子中的整数910//将整数转换为16进制字符串11QStringhexString=QString::n......
  • 初学可视化PyQt5
    【初学可视化PyQt5系列】第1章PyQt5简介第2章PyQt5新增功能第3章Hellomyfourrotordrone第4章PyQt5主要类第5章PyQt5使用Qt设计器第6章PyQt5信号与插槽第7章PyQt5布局与管理第8章PyQt5基本小部件第9章PyQt5QDialog类第10章PyQt5QMessageBox......