Qt实现TFTP Server和 TFTP Client(二)

3 实现

3.1 Core

Core模块包括下面4个类:

  • TFTP
  • BaseUdp
  • TFtpClientFile
  • TFtpServerFile
3.1.1 TFTP

TFTP类实现了TFTP协议。

3.1.1.1 TFTP定义
class TFtp
{
public:TFtp();enum Code {RRQ   = 0x0001,//Read requestWRQ   = 0x0002,//Write requestDATA  = 0x0003,//Data requestACK   = 0x0004,//AcknowledgementERROR = 0x0005 //Error};enum Mode { BINARY, ASCII, MAIL };enum Error {NotDefined        = 0x0000,FileNotFound      = 0x0001,AccessViolation   = 0x0002,DiskFull          = 0x0003,IllegalOperation  = 0x0004,UnknownTransferID = 0x0005,FileExists        = 0x0006,NoSuchUser        = 0x0007,};enum Size {CODE_SIZE = 2,HEADER_SIZE = 4,BLOCK_SIZE  = 512};enum Type { None, Read, Write };bool process(uint8_t const *data, uint32_t size);bool is_finished() const { return finished_; }bool is_error() const { return !error_msg_.empty(); }Error error() const { return error_; }std::string error_msg() const { return error_msg_; }
protected:virtual void on_read_req(std::string const& filename, Mode mode) {}virtual void on_write_req(std::string const& filename, Mode mode) {}virtual void on_data(uint16_t block_number, uint8_t const*data, uint32_t size) = 0;virtual void on_ack(uint16_t block_number) = 0;virtual void on_error(uint16_t error, std::string const& error_msg) = 0;virtual uint32_t write(uint8_t const *data, size_t size) = 0;void read_req(std::string const& filename, Mode mode);void write_req(std::string const& filename, Mode mode);void send(uint16_t block_number, size_t size);void resend();void ack(uint16_t block_number);void error(Error error, std::string const& error_msg);char *data() { return (char *)(data_ + HEADER_SIZE); }void set_error(Error error, std::string const& error_msg){error_ = error;error_msg_ = error_msg;}void finished() { finished_ = true; }size_t get_filesize(const char*filename);
private:uint16_t op_code(uint8_t const *data) { return static_cast<uint16_t>((data[0] << 8) | data[1]); }uint16_t block_num(uint8_t const *data) { return static_cast<uint16_t>((data[2] << 8) | data[3]); }uint16_t error_code(uint8_t const *data) { return static_cast<uint16_t>((data[2] << 8) | data[3]); }Mode getMode(std::string const& text);std::string getText(Mode mode);
private:uint8_t data_[HEADER_SIZE + BLOCK_SIZE];bool finished_ = false;TFtp::Error error_ = NotDefined;std::string error_msg_;size_t block_size_ = 0;
};

成员函数说明:

  • process 解析数据包为TFtp::Code的请求包
  • on_read_req 读请求虚函数,TFtpServerFile重载此函数
  • on_write_req 写请求虚函数,TFtpServerFile重载此函数
  • on_data 数据请求虚函数,TFtpServerFile/TFtpClientFile重载此函数
  • on_ack 应答请求虚函数,TFtpServerFile/TFtpClientFile重载此函数
  • on_error 出错处理虚函数,TFtpServerFile/TFtpClientFile重载此函数
  • write 写数据虚函数,TFtpServerFile/TFtpClientFile重载此函数
  • read_req 构造读请求数据包并发送
  • write_req 构造写请求数据包并发送
  • send 构造数据包请求并发送
  • resend 重发数据包
  • ack 构造应答请求数据包并发送
  • error 构造出错数据包并发送
3.1.1.2 TFTP实现
  • process/getMode
#define MIN_PACKET_LEN 4
bool TFtp::process(uint8_t const *data, uint32_t size)
{if(size < MIN_PACKET_LEN)return false;uint16_t code = op_code(data);if(code == RRQ || code == WRQ){uint8_t const*d = data + sizeof(uint16_t);uint8_t const*e = data + size;uint8_t const*s = d;while(s < e && *s)s++;std::string filename((char *)d, s - d);s++;d = s;while(s < e && *s)s++;std::string mode_text((char *)d, s - d);if(code == RRQ)on_read_req(filename, getMode(mode_text));elseon_write_req(filename, getMode(mode_text));return true;}else if(code == DATA){on_data(block_num(data), &data[HEADER_SIZE], size - HEADER_SIZE);return true;}else if(code == ACK){on_ack(block_num(data));return true;}else if(code == ERROR){uint8_t const* d = data + HEADER_SIZE;uint8_t const *e = data + size;uint8_t const *s = d;while(s < e && *s)s++;on_error(error_code(data), std::string((char *)d, s - d));return true;}return false;
}
TFtp::Mode TFtp::getMode(std::string const& text)
{if(text == "octet")return BINARY;else if(text == "netascii")return ASCII;elsereturn MAIL;
}

函数流程:

  1. 判断数据包长度小于最小长度4,解析失败返回
  2. 获取数据包的code。
  3. 根据code类型解析数据调用对应的接口函数。
  • read_req/write_req/send/resend/ack/error
#define WITE_CODE(data, code) \data[0] = uint8_t(code >> 8); \data[1] = uint8_t(code >> 0);#define WITE_HEAD(data, code, value) \data[0] = uint8_t(code >> 8); \data[1] = uint8_t(code >> 0); \data[2] = uint8_t(value >> 8);\data[3] = uint8_t(value >> 0);void TFtp::read_req(std::string const& filename, Mode mode)
{std::string text = getText(mode);std::vector<uint8_t> data(CODE_SIZE + filename.size() + text.size() + 2, 0);WITE_CODE(data, RRQ)memcpy(&data[CODE_SIZE], filename.c_str(), filename.size());memcpy(&data[CODE_SIZE + filename.size() + 1], text.c_str(), text.size());write(&data[0], data.size());
}void TFtp::write_req(std::string const& filename, Mode mode)
{std::string text = getText(mode);std::vector<uint8_t> data(CODE_SIZE + filename.size() + text.size() + 2, 0);WITE_CODE(data, WRQ)memcpy(&data[CODE_SIZE], filename.c_str(), filename.size());memcpy(&data[CODE_SIZE + filename.size() + 1], text.c_str(), text.size());write(&data[0], data.size());
}void TFtp::send(uint16_t block_number, size_t size)
{WITE_HEAD(data_, DATA, block_number)block_size_ = size;write(data_, size + HEADER_SIZE);
}void TFtp::resend()
{write(data_, block_size_ + HEADER_SIZE);
}void TFtp::ack(uint16_t block_number)
{std::vector<uint8_t> data(HEADER_SIZE);WITE_HEAD(data, ACK, block_number)write(&data[0], data.size());
}void TFtp::error(Error error, std::string const& error_msg)
{std::vector<uint8_t> data(HEADER_SIZE + error_msg.size() + 1);error_ = error;error_msg_ = error_msg;finished();WITE_HEAD(data, ERROR, error)memcpy(&data[HEADER_SIZE], error_msg.c_str(), error_msg.size());data[data.size() - 1] = 0;write(&data[0], data.size());
}

函数说明:根据请求类型构造对应的请求包并发送。

3.1.2 BaseUdp
class BaseUdp
{
public:virtual ~BaseUdp(){}virtual uint32_t write(const char* data, size_t size) = 0;
};

类型说名:

  • 定义udp的写接口,该接口需要TFtpServer和TFtpClient去实现。
3.1.3 TFtpClientFile

TFtpClientFile类实现客户端文件收发

3.1.3.1 TFtpClientFile定义
class TFtpClientFile : public TFtp
{
public:TFtpClientFile(BaseUdp *udp): udp_(udp), type_(None){}~TFtpClientFile();bool getFile(std::string const& local_filename,std::string const& remote_filename, Mode mode);bool putFile(std::string const& local_filename,std::string const& remote_filename, Mode mode);size_t filesize() const { return filesize_; }size_t file_bytes() const { return file_bytes_; }using Ptr = std::shared_ptr<TFtpClientFile>;
protected:void on_data(uint16_t block_number, uint8_t const* data, uint32_t size) override;void on_ack(uint16_t block_number) override;void on_error(uint16_t error, std::string const& error_msg) override;uint32_t write(uint8_t const *data, size_t size) override;
private:void send_data(uint16_t block_number);
private:BaseUdp* udp_;Type type_;std::ifstream read_file;std::ofstream write_file;uint16_t block_number_ = 0;uint32_t block_size_ = 0;size_t filesize_ = 0;size_t file_bytes_ = 0;
};

成员函数说明:

  • getFile 下载文件
  • putFile 上传文件
  • on_data 实现数据请求
  • on_ack 实现应答请求
  • on_error 实现出错处理
  • write 实现写功能
  • send_data 从文件读取数据包并发送。
3.1.3.2 TFtpClientFile实现
  • getFile
bool TFtpClientFile::getFile(std::string const& local_filename,std::string const& remote_filename,Mode mode)
{if(mode == TFtp::BINARY)write_file.open(local_filename.c_str(),std::ifstream::out | std::ifstream::binary);elsewrite_file.open(local_filename.c_str());if(!write_file.is_open())return false;read_req(remote_filename, mode);type_ = Write;return true;
}

函数流程:

  • 以写方式打开本地文件

  • 发送读文件请求

  • 将类型设置为写

  • putFile

    if(mode == TFtp::BINARY)read_file.open(local_filename.c_str(),std::ifstream::in | std::ifstream::binary);elseread_file.open(local_filename.c_str());if(!read_file.is_open())return false;filesize_ = get_filesize(local_filename.c_str());write_req(remote_filename, mode);type_ = Read;return true;

函数流程:

  • 以读方式打开本地文件

  • 发送写文件请求

  • 将类型设置为读

  • on_data

void TFtpClientFile::on_data(uint16_t block_number, uint8_t const* data, uint32_t size)
{if(type_ != Write){error(IllegalOperation, "Illegal TFTP Operation in Data");return;}if(block_size_ == 0)block_size_ = size;write_file.write((char *)data, size);file_bytes_ += size;ack(block_number);if(size < block_size_){filesize_ = file_bytes_;finished();write_file.close();}
}

函数流程:

  • 保存数据包

  • 发送应答

  • 处理最后一个包

  • 下载结束

  • on_ack

void TFtpClientFile::on_ack(uint16_t block_number)
{if(type_ != Read){error(IllegalOperation, "Illegal TFTP Operation in ACK");return;}if(read_file.eof()){std::cout << "send data is finished" << std::endl;finished();return;}if(block_number_ != block_number)resend();else{block_number_++;send_data(block_number_);}
}

函数流程:

  • 如果文件上传完毕,结束上传。

  • BlockNumber不同,则重传。

  • 上传下一Block。

  • on_error

void TFtpClientFile::on_error(uint16_t error, std::string const& error_msg)
{set_error((Error)error, error_msg + std::string(" come from remote"));finished();
}
  • send_data
void TFtpClientFile::send_data(uint16_t block_number)
{char* d = data();read_file.read(d, TFtp::BLOCK_SIZE);file_bytes_ += read_file.gcount();send(block_number, read_file.gcount());
}
  • write
uint32_t TFtpClientFile::write(uint8_t const *data, size_t size)
{return udp_->write((const char*)data, size);
}
3.1.4 TFtpServerFile

TFtpServerFile类实现服务端文件收发

3.1.4.1 TFtpServerFile定义
class TFtpServerFile : public TFtp
{
public:TFtpServerFile(BaseUdp *udp, std::string const& path, std::string const& id): udp_(udp), type_(None), file_path_(path), transfer_id_(id), block_number_(0){}~TFtpServerFile();using Ptr = std::shared_ptr<TFtpServerFile>;std::string transfer_id() const { return transfer_id_; }Type type() const { return type_; }std::string filename() const { return filename_; }uint16_t block_number() const { return block_number_; }uint16_t block_numbers() const { return static_cast<uint16_t>((filesize_ + BLOCK_SIZE - 1) / BLOCK_SIZE); }size_t filesize() const { return filesize_; }size_t file_bytes() const { return file_bytes_; }
protected:void on_read_req(std::string const& filename, Mode mode) override;void on_write_req(std::string const& filename, Mode mode) override;void on_data(uint16_t block_number, uint8_t const* data, uint32_t size) override;void on_ack(uint16_t block_number) override;void on_error(uint16_t error, std::string const& error_msg) override;uint32_t write(uint8_t const *data, size_t size) override;
private:void send_data(uint16_t block_number);std::string full_fileaname(std::string const& filename) const {return file_path_ + filename;}TFtpServerFile(TFtpServerFile const&);TFtpServerFile(TFtpServerFile &&);TFtpServerFile operator == (TFtpServerFile const&);TFtpServerFile operator == (TFtpServerFile &&);
private:BaseUdp* udp_;Type type_;std::string filename_;std::string file_path_;std::string transfer_id_;std::ifstream read_file;std::ofstream write_file;uint16_t block_number_;uint32_t block_size_ = 0;size_t filesize_ = 0;size_t file_bytes_ = 0;
};

成员函数说明:

  • transfer_id 返回唯一传输ID,用来管理多个TFtpServerFile实例。
  • on_read_req 实现读请求。
  • on_write_req 实现写请求。
  • on_data 实现数据请求。
  • on_ack 实现应答请求。
  • on_error 实现出错处理。
  • write 实现写功能。
  • send_data 从文件读取数据包并发送。
3.1.4.2 TFtpServerFile实现
  • on_read_req
void TFtpServerFile::on_read_req(std::string const& filename, Mode mode)//read
{if(type_ != None){error(IllegalOperation, "Illegal TFTP Operation in RRQ");return;}type_ = Read;filename_ = full_fileaname(filename);if(mode == TFtp::BINARY)read_file.open(filename_.c_str(),std::ifstream::in | std::ifstream::binary);elseread_file.open(filename_.c_str());if(!read_file.is_open())error(FileNotFound,  std::string("File(") + filename + std::string(") Not Found"));else{block_number_ = 1;filesize_ = get_filesize(filename_.c_str());send_data(block_number_);}
}

函数流程:

  • 以读方式打开文件

  • 设置类型为读

  • 发送第一个Block数据

  • on_write_req

void TFtpServerFile::on_write_req(std::string const& filename, Mode mode)//write
{if(type_ != None){error(IllegalOperation, "Illegal TFTP Operation in WRQ");return;}filename_ = full_fileaname(filename);if(get_filesize(filename_.c_str()) > 0){error(FileExists, "File Exists in WRQ");return;}type_ = Write;if(mode == TFtp::BINARY)write_file.open(filename_.c_str(),std::ifstream::out | std::ifstream::binary);elsewrite_file.open(filename_.c_str());if(!write_file.is_open())error(AccessViolation, "Access Violation");elseack(block_number_);//ack(0)
}

函数流程:

  • 以写方式打开文件。

  • 设置类型为写

  • 请求第一块数据

  • on_data

void TFtpServerFile::on_data(uint16_t block_number, uint8_t const* data, uint32_t size) //write
{if(type_ != Write){error(IllegalOperation, "Illegal TFTP Operation in Data");return;}if(block_number != block_number_ + 1)ack(block_number_);else{if(block_size_ == 0)block_size_ = size;write_file.write((char *)data, size);file_bytes_ += size;ack(block_number);block_number_ = block_number;if(size < block_size_){filesize_ = file_bytes_;write_file.close();finished();}}
}

函数流程:

  • Block号不一致,则请求前一个块号。

  • 保存数据包

  • 保存最后数据包,

  • 上传结束

  • on_ack

void TFtpServerFile::on_ack(uint16_t block_number) // read
{if(type_ != Read){error(IllegalOperation, "Illegal TFTP Operation in ACK");return;}if(read_file.eof()){finished();return;}if(block_number != block_number_)resend();else{block_number_++;send_data(block_number_);}
}

函数流程:

  • 如果文件发送完毕,则结束

  • BlockNumber不一致,则重传

  • 增加BlockNumber,发送Block数据

  • on_error

void TFtpServerFile::on_error(uint16_t error, std::string const& error_msg) //read/write
{set_error((Error)error, error_msg + std::string(" come from remote"));finished();
}
  • write
uint32_t TFtpServerFile::write(uint8_t const *data, size_t size)
{return udp_->write((const char*)data, size);
}
  • send_data
void TFtpServerFile::send_data(uint16_t block_number)
{char* d = data();read_file.read(d, TFtp::BLOCK_SIZE);file_bytes_ += read_file.gcount();send(block_number, read_file.gcount());
}

Qt实现TFTP Server和 TFTP Client(一)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/771016.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows服务器性能监控

Windows服务器操作系统设计用于运行在客户端-服务器架构内的服务器上&#xff0c;这些服务器通常设计用于处理繁重的工作负载&#xff0c;并作为企业中涉及的大多数软件操作的骨干。因此&#xff0c;为了防止由于性能问题而导致的任何服务损失并保持操作的无缝流&#xff0c;Wi…

如何评价代码质量

设计模式专栏&#xff1a; http://t.csdnimg.cn/4Mt4u 目录 1.引言 2.可维护性(maintainability) 3.可读性(readability) 4.可扩展性(extensibility) 5.灵活性(flexibility) 6.简洁性(simplicity) 7.可复用性(reusability) 8.可测试性(testability) 9.总结 1.引…

Linux-进程控制(进程创建、进程终止、进程等待)

一、进程创建 1.1 fork函数介绍 在命令行下我们可以通过 ./ exe文件 来创建一个进程&#xff0c;通过fork函数&#xff0c;我们可以通过代码的形式从一个进程中创建一个进程&#xff0c;新进程为子进程&#xff0c;原进程为父进程&#xff0c;子进程在创建时&#xff0c;会与…

教育建筑智慧能源管理平台解决方案【新型电力系统下的绿色校园能源管理平台】

一、行业特点 1.建筑类型多&#xff1a;集教学、科研、生活于一体&#xff0c;占地面积大&#xff0c;建筑类型多&#xff0c;功能划分复杂。 2.供电可靠性要求高&#xff1a;教育建筑中的高层建筑、图书馆、实验楼等特级和一级负荷比较多&#xff0c;一旦发生故障会危及生命…

STM32 ESP8266模块的曲折探索

这是本文的配套资料&#xff0c;最终工程请参考 新_ESP8266资料\stm32f103成功移植的项目 【免费】stm32f103c8t6esp8266资料资源-CSDN文库 一、等到了ready 产品参数 我使用的是ai-thinker的esp8266-01s&#xff0c;以下为产品规格书 引脚定义&#xff1a; 依据引脚定义&…

ssh -p 2222怎么进docker容器

要通过SSH和端口2222进入Docker容器&#xff0c;您需要确保容器内已经安装并运行了SSH服务器&#xff0c;并且已经将宿主机的2222端口映射到容器的SSH端口&#xff08;通常为22&#xff09;。以下是一般的步骤&#xff1a; 1、启动容器时映射端口&#xff1a; 当您启动容器时…

android studio忽略文件

右键文件&#xff0c;然后忽略&#xff0c;就不会出现在commit里面了 然后提交忽略文件即可

Linux查询日志常用命令整理

Linux查询日志常用命令整理 1. 实时查看日志2. 查看历史日志的最后几行3. 根据关键词过滤日志4. 查询指定路径下的所有日志文件5. 当日志文件过大时&#xff0c;查看开头部分6. 筛选出指定时间范围内的日志7. 分页查看日志8. 将查询到的日志输出到另一个文件9. 查看过去某一时间…

如何在VS Code上搭建 C/C++开发环境

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是VScode VScode&#xff08;Visual Studio Code&#xff09;是一款由微软开发的免费开源的轻量级代码编辑器。它…

AI绘画自动生成器有哪些?

AI绘画自动生成器近年来发展迅速&#xff0c;以下是一些知名的和受欢迎的AI绘画工具&#xff1a; DALL-E2 - 由OpenAI开发&#xff0c;可以依据文本描述生成高度逼真的图像。Deep Dream Generator - 使用深度学习技术对上传的图片进行艺术化处理。Artbreeder - 提供图像合成和…

鸿蒙 ohpm 的异常报错

解压安装 ohpm , 进入 command-line-tools/ohpm/bin 目录执行 ohpm -v , 一直提示未初始化异常&#xff1a;ERROR: ohpm has not been initialized yet. Execute the init script to initialize it first. google搜索发现都是让配置环境变量、执行init脚本&#xff0c;尝试后…

Python操作Sqlite的简单封装

文章目录 一、安装依赖二、配置文件三、实现类 一、安装依赖 pip install numpy二、配置文件 utils.config.py ############### 233 SQLITE Configuration ############### SQLITE_PATH ./mysqlite.db三、实现类 utils.PostGreOp.py # encoding: utf-8import json import …

如何成功将自己开发的APP上架到应用商店

随着移动应用市场的蓬勃发展&#xff0c;开发一款优秀的APP已成为许多企业和个人的首要选择。然而&#xff0c;成功上架并有效推广APP至关重要。本文将逐步介绍完整的上架流程&#xff0c;包括准备所需材料、注册开发者账户、进行APP备案、提交审核以及上架成功后的推广和维护。…

maya 重定向 pycharm运行

目录 maya sdk下载: 添加sdk 依赖库: pycharm连接 maya 测试ok maya重定向脚本 插

电子商务营销中大数据分析|电商大数据采集API接口的应用

随着经济的不断发展&#xff0c;网络信息技术不断加强&#xff0c;电子商务和大数据的蓬勃发展极大地方便了人们的生活。本文章主要阐述大数据分析与电商营销的含义、大数据分析在电子商务营销中的应用&#xff0c;以及该应用的作用和存在哪些不足及解决方法。探究大数据分析在…

【MATLAB源码-第14期】基于matlab的2ASK的误码率BER仿真以及原信号调制信号解调信号波形展示。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 幅度偏移调制&#xff0c;又称幅移键控&#xff0c;幅度键移&#xff08;英语&#xff1a;Amplitude-shift keying&#xff0c;ASK&#xff09;是通过载波的幅度变化来表示数字信号的一种幅度调制方式。在一个ASK系统中&…

Web日志分析

一 、HTTP基础 1. HTTP报文格式解析 HTTP请求报文 HTTP请求包括3部分&#xff0c;分别是请求行、请求头和请求正文。 Windows NT 10.0表示操作系统内核版本号&#xff0c;Windows XP内核号是NT 5.1或NT 5.2&#xff08;64位操作系统)&#xff0c;Windows Vista的内核版本号是…

mybatisPlus动态sql语句 ${ew.customSqlSegment}

1.Mapper层 List<SmsSendTaskVO> queryList(Param("ew")Wrapper wrapper, DataScope dataScope); 2.sql语句 Select(" select t.submit_num,t.sms_charge_num ${ew.customSqlSegment}”) ${ew.customSqlSegment}是MyBatis Plus提供的动态SQL语句拼接功能…

Nebula Graph-06-NebulaGraph Java 使用 和SpringBoot集成Nebula Graph

前言 系列文章&#xff1a; Nebula Graph-01-Nebula Graph简介和安装以及客户端连接 Nebula Graph-02-NebulaGraph高阶配置、用户管理、日志 Nebula Graph-03-NebulaGraph Studio-可视化web工具安装和使用 Nebula Graph-04-NebulaGraph nGQL的介绍和使用 Nebula Graph-05-Nebu…

【干货】Apache DolphinScheduler2.0升级3.0版本方案

升级背景 因项目需要使用数据质量模块功能&#xff0c;可以为数仓提供良好的数据质量监控功能。故要对已有2.0版本升级到3.0版本以上&#xff0c;此次选择测试了3.0.1 和 3.1.1 两个版本&#xff0c;对进行同数据等任务调度暂停等操作测试&#xff0c;最后选择3.0.1 版本 原因…