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