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

Linux网络编程 深入解析TFTP协议:基于UDP的文件传输实战

知识点1【TFTP的概述】

学习通信的基本:通信协议(具体发送上面样的报文)、通信流程(按照什么步骤发送)

1、TFTP的概述

tftp:简单文件传输协议,**基于UDP,**不进行用户有效性验证

基于UDP:编程架构是UDP的

必须创建UDP套接字

套接字分类:UDP套接字,TCP套接字,原始套接字

数据传输格式(模式选择):

octet:二进制模式

netascii:文本模式

2、TFTP的通信过程

决定了 编程流程

我们主要写客户端

注意:

服务器中,在69号端口后,是临时端口,临时端口是只要socket,不bind。因此需要创建一个新的线程,线程中执行socket()

TFTP通信过程总结

3、TFTP协议分析

读写请求是用户自己发送的,我们现在不考虑选项部分。

数据包516个字节,当小于516个字节的时候,表示下载 结束(最后一个报文),块编号中存储的是该数据包的编号。这里注意一定要对操作码进行判断,是3才可以确认是数据包。

收到数据包 我们需要回复一个ACK,ACK中的块编号需要和数据包的ACK相同

这里有一个技巧,客户端直接把收到的内容的前4个字节发送给服务器,只需要修改操作码的第二个字节即可

错误码操作码是5,差错码是不同的错误有不同的编号,差错信息说明错误。

OACK是比如我们设置了选项部分,需要设置的,相对复杂,我们下面介绍。

想一想

传输的数据的大小一定是 512Byte 吗?

由于网络的原因,一方收不到另一方的数据怎么办?

1、不一定需要512,选项修改

2、这里服务器发送数据包后,如果没有收到ACK,它会等待,并从发送数据包处开始计时,超出 超时时间后,才会发出错误码,提示重发。

而这里的数据包长度修改,超时时间的修改,都需要借助选项来完成

4、TFTP带选项的读写报文

选项和值成对出现。

OACK是在修改了选项后,服务器向客户端发送的一个报文,是起一个确认作用的。

我们结合上图,客户端一旦修改了选项部分,客户端不再是立即发送数据,而是发送OACK,让客户端确认是否要这样修改(通过ACK,块编号中:0表示同意,这也是为什么数据编号是从1开始的),客户端给予回应后,服务器才会执行发送数据的操作。

选项的种类

tsize 选项

当读操作时,tsize 选项的参数必须为“0”,服务器会返回待读取的文件的大小

当写操作时,tsize 选项参数应为待写入文件的大小,服务器会回显该选项

blksize 选项

修改传输文件时使用的数据块的大小(范围:8~65464)

timeout 选项

修改默认的数据传输超时时间(单位:秒)

注意:

1、选项没有顺序

2、在使用tsize 读操作的时候,tsize的默认参数是0(因为读时不知道文件的大小),收到OACK的时候,会返回实际文件的大小(nB说明是字符串需要转换)

总结

1、可以通过发送带选项的读/写请求发送给 server 2、如果 server 允许修改选项则发送选项修改确认包 3、server 发送的数据、选项修改确认包都是临时 port 4、server 通过 timeout 来对丢失数据包的重新发送

知识点2【TFTP案例】

需求:编写TFTP客户端(不带选项),从客户端下载文件

流程:

1、创建UDP套接字

2、构建读请求报文,并发送到服务器69号端口

3、while 循环接收,buf是516个字节,recvform的返回值是获取到数据的长度

这里需要创建一个文件描述符取接收

4、关闭文件描述符 和 套接字

构建 读写请求报文 的方式

使用sprintf组包,当sendto时,有一个内容长度,使用sprintf的返回值:组包的总长度,目的端口写69

代码演示

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(int argc, char const *argv[])
{if(argc != 3){printf("error\\n");printf("modo:./a.out 196.168.9.28 a.txt\\n");_exit(-1);}//创建套接字,目的:向服务器发送数据int fd_sock = socket(AF_INET,SOCK_DGRAM,0);if(fd_sock < 0){perror("socket");return 0;}//sprintf组包,返回值使用len_sprintf接收。格式: 0 1 文件名 0 模式 0 char arr_sprintf[64] = "";int len_sprintf = sprintf(arr_sprintf,"%c%c%s%c%s%c",0,1,argv[2],0,"octet",0);//发送sprintf组包(读写报文)struct sockaddr_in addr_rw;memset(&addr_rw,0,sizeof(addr_rw));addr_rw.sin_family = AF_INET;addr_rw.sin_port = htons(69);addr_rw.sin_addr.s_addr = inet_addr(argv[1]);sendto(fd_sock,arr_sprintf,len_sprintf,0,(struct sockaddr *)&addr_rw,sizeof(addr_rw));/*  接收服务器发出的 数据报文,报文的前四个字节:操作码(2),端口码(2),数据(512),存储在arr_recv中数据段操作码判断,下面是3的操作重复性判断,定义一个unsigned short num = 0,num+1与端口码比较数据写入文件,并将端口码赋值给numACK 回应服务器 arr_recv前四个字节进行处理即可*///操作码为5操作//关闭套接字 及 文件描述符 //输出错误原因unsigned short num = 0;int fd_w = open(argv[2],O_WRONLY | O_CREAT,0664);if(fd_w < 0){close(fd_sock);perror("open");return 0;}int len = 0;unsigned char arr_recv[516] = "";struct sockaddr_in addr_recv;int len_recv = sizeof(addr_recv);do{len = recvfrom(fd_sock,arr_recv,sizeof(arr_recv),0,(struct sockaddr *)&addr_recv,&len_recv);if(arr_recv[1] == 3){if((unsigned short)(num + 1) == ntohs(*(unsigned short *)(arr_recv +2))){write(fd_w,arr_recv + 4,len - 4);//ack应答num = ntohs(*(unsigned short *)(arr_recv +2));printf("%d\\n",num);}arr_recv[1] = 4;sendto(fd_sock,arr_recv,4,0,(struct sockaddr *)&addr_recv,sizeof(addr_recv));}else if(arr_recv[1] == 5){close(fd_sock);close(fd_w);unlink(argv[2]);printf("%s\\n",arr_recv+4);_exit(-1);}} while (len == 516);//关闭文件描述符,套接字,关闭文件close(fd_w);close(fd_sock);return 0;
}

代码运行结果

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏夹关注,谢谢大家!!!

http://www.xdnf.cn/news/99.html

相关文章:

  • jQuery — DOM与CSS操作
  • 使用 PySpark 批量清理 Hive 表历史分区
  • Layui Table组件,设置data数据源,以及page为False,表格只能显示10条数据的问题
  • Spring Boot日志系统详解:Logback与SLF4J的默认集成
  • J值即正义——Policy Gradient思想、REINFORCE算法,以及贪吃蛇小游戏(三)
  • JVM对象创建全过程
  • 大模型面经 | DeepSpeed中ZeRO-1、ZeRO-2和ZeRO-3的区别是什么?
  • uniapp运行在app端如何使用缓存
  • 【ubuntu】在Linux Yocto的基础上去适配Ubuntu的wifi模块
  • 科技如何改变世界?
  • 微博辐射源和干扰机
  • Hadoop的三大结构及其作用
  • leetcode 309. Best Time to Buy and Sell Stock with Cooldown
  • 热门与冷门并存,25西电—电子工程学院(考研录取情况)
  • 如何在米尔-STM32MP257开发板上部署环境监测系统
  • Windows 图形显示驱动开发-WDDM 1.2功能—Windows 8 中的 DirectX 功能改进(五)
  • 什么是单元测试的“覆盖率”
  • 计算机视觉——基于使用 OpenCV 与 Python 实现相机标定畸变校正
  • 安全测试报告模板
  • PyTorch 浮点数精度全景:从 float16/bfloat16 到 float64 及混合精度实战
  • pnpm解决幽灵依赖问题
  • [Unity]-[UI]-[Prefab] 关于UGUI UI Prefab的制作技巧
  • C++: 类和对象(中)
  • 避免IP地址关联,多个手机设备的完美公网IP问题
  • Django ORM 定义模型
  • 【html】a标签target属性以及扩展应用
  • 2025TGCTF Web WP复现
  • 2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(六级)答案 + 解析
  • 多线程编程的简单案例——单例模式[多线程编程篇(3)]
  • 前端零基础入门到上班:Day7——表单系统实战全解析