当前位置: 首页 > news >正文

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(一)   Qt实现TFTP Server和 TFTP Client(三)

http://www.lryc.cn/news/326659.html

相关文章:

  • 【b站李炎恢】Vue.js Element UI | 十天技能课堂 | 更新中... | 李炎恢
  • AI大语言模型工程师学习路线
  • 基于树莓派实现 --- 智能家居
  • 基于Arduino IDE 野火ESP8266模块 一键配网 的开发
  • 左手医生:医疗 AI 企业的云原生提效降本之路
  • ceph集群部署
  • C#WPF控件Label宽度绑定到父控件的宽度
  • HMI的学习
  • 工业无线网关在汽车制造企业的应用效果和价值-天拓四方
  • 校园app开发流程-uniapp开发-支持APP小程序H5-源码交付-跑腿-二手市场-交友论坛等功能,学校自由选择!
  • Machine Learning机器学习之K近邻算法(K-Nearest Neighbors,KNN)
  • 四、在数据库里建库
  • 蓝桥杯-网络安全比赛(2)基础学习-正则表达式匹配电话号码、HTTP网址、IP地址、密码校验
  • 如何创建azure pipeline
  • 缓存菜品、套餐、购物车相关功能
  • 微信小程序的页面交互1
  • win10 docker zookeeper和kafka搭建
  • 【Redis】快速入门 数据类型 常用指令 在Java中操作Redis
  • 【tingsboard开源平台】下载数据库,IDEA编译,项目登录
  • Web3:探索区块链与物联网的融合
  • [BT]BUUCTF刷题第9天(3.27)
  • html页面使用@for(){},@if(){},利用jquery 获取当前class在列表中的下标
  • pulsar: 批量接收消息
  • LNMP架构之mysql数据库实战
  • aws使用记录
  • 区块链食品溯源案例实现(二)
  • RabbitMQ(简单模式)
  • ES集群部署的注意事项
  • Etcd 基本入门
  • PPT没保存怎么恢复?3个方法(更新版)!