0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于UDP的简单文件传输协议TFTP设计

CHANBAEK 来源:木南创智 作者:尹家军 2022-12-14 15:06 次阅读

前面我们已经实现了UDP的回环客户端和回环服务器的简单应用,接下来我们实现一个基于UDP的简单文件传输协议TFTP。

1 TFTP****协议简介

TFTP是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69

TFTP是一种简单的文件传输协议。目标是在UDP之上上建立一个类似于FTP的但仅支持文件上传和下载功能的传输协议,所以它不包含FTP协议中的目录操作和用户权限等内容。

TFTP报文的头两个字节表示操作码,共有5中操作码,如下表:

读请求和写请求功能码的数据报文格式是一样的,所以TFTP报文又可表述为4种形式。对于读请求或者写请求,文件名字段说明客户要读或写的位于服务器的上的文件并以0字节作为结束,模式字段是一个ASCII码串,同样以0字节结束。读请求和写请求的报文格式:

其次是数据包,起包括2个字节的块编号以及0-512个字节的数据信息。数据包相对比较简单,其报文格式:

再者为确认包。确认包也有2个字节的块编号。其数据格式:

最后一种TFTP报文类型是差错报文,它的操作码为5.它用于服务器不能处理读请求或者写请求的情况。在文件传输的过程中的读和写也会导致传送这种报文,接着停止传输。错误包的报文格式:

TFTP的工作过程很像停止等待协议,发送完一个文件块后就等待对方的确认,确认时应指明所确认的块号。发送完数据后在规定时间内收不到确认就要重发数据PDU,发送确认PDU的一方若在规定时间内收不到下一个文件块,也要重发确认PDU。这样保证文件的传送不致因某一个数据报的丢失而告失败。

2 TFTP****协议栈设计

前面我们简单的介绍了TFTP协议,接下来我们看看该如何实现其编程。它有5种操作码,我们要做的就是实现对这5种操作码的响应。

2.1 、读请求实现

所谓读请求,就是客户端请求从服务器获取文件,那么服务器需要做的自然是响应客户端的请求。但我们并没有文件,所以不管它请求什么文件,我们均给它返回内容和大小相同的测试文件。

1 /* TFTP读请求处理*/
 2 int TftpReadProcess(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, char* FileName)
 3 {
 4   tftp_connection_args *args = NULL;
 5  
 6   /* 这个函数在回调函数中被调用,因此中断被禁用,因此我们可以使用常规的malloc */
 7   args = mem_malloc(sizeof(tftp_connection_args));
 8  
 9   if (!args)
10   {
11     /* 内存分配失败 */
12     SendTftpErrorMessage(upcb, to, to_port, TFTP_ERR_NOTDEFINED);
13  
14     CleanTftpConnection(upcb, args);
15  
16     return 0;
17   }
18  
19   /* i初始化连接结构体  */
20   args->op = TFTP_RRQ;
21   args->remote_port = to_port;
22   args->block = 1; /* 块号从1开始 */
23   args->tot_bytes = 10*1024*1024;
24  
25   /* 注册回调函数 */
26   udp_recv(upcb, RrqReceiveCallback, args);
27  
28   /* 通过发送第一个块来建立连接,后续块在收到ACK后发送*/
29    SendNextBlock(upcb, args, to, to_port);
30  
31   return 1;
32 }

2.2 、写请求实现

写请求就是客户端希望向服务器传送文件,在这里我们只是实现TFTP服务器的功能,没必要将收到的文件真正保存到一个地方,所以只是做接收文件的过程并不将其写到存储器,简单的说就是只在内存中而不会写入Flash等。

1 /* TFTP写请求处理 */
 2 int TftpWriteProcess(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, char *FileName)
 3 {
 4   tftp_connection_args *args = NULL;
 5  
 6   /* 这个函数在回调函数中被调用,因此中断被禁用,因此我们可以使用常规的malloc */
 7   args = mem_malloc(sizeof(tftp_connection_args));
 8  
 9   if (!args)
10   {
11     SendTftpErrorMessage(upcb, to, to_port, TFTP_ERR_NOTDEFINED);
12  
13     CleanTftpConnection(upcb, args);
14  
15     return 0;
16   }
17  
18   args->op = TFTP_WRQ;
19   args->remote_port = to_port;
20   args->block = 0;      //WRQ响应的块号为0
21   args->tot_bytes = 0;
22  
23   /* 为控制块注册回调函数 */
24   udp_recv(upcb, WrqReceiveCallback, args);
25  
26   /* 通过发送第一个ack来发起写事务 */
27   SendTftpAckPacket(upcb, to, to_port, args->block);
28  
29   return 0;
30 }

2.3 、数据包操作

无论是读请求还是写请求,最终的目的无非是要传送数据,所以数据包自然也是我们需要构造和传送的。其对应的就是数据包操作码,我们设计程序如下:

1 /* 构造并且传送数据包 */
 2 static int SendTftpDataPacket(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, int block,char *buf, int buflen)
 3 {
 4   /* 将开始的2个字节设置为功能码 */
 5   SetTftpOpCode(buf, TFTP_DATA);
 6  
 7   /* 将后续2个字节设置为块号 */
 8   SetTftpBlockNumber(buf, block);
 9  
10   /* 在后续设置n个字节的数据 */
11  
12   /* 发送数据包 */
13   return SendTftpMessage(upcb, to, to_port, buf, buflen + 4);
14 }

2.4 、确认包操作

在传送数据包后,收到没收到,发送方是不知道的,怎么办呢?这时候接受方接收到后,会给出一个确认包。其对应的就是确认操作码,那么我们还需实现确认包的构造和发送。

1 /*构造并发送确认包*/
 2 int SendTftpAckPacket(struct udp_pcb *upcb,const ip_addr_t *to, int to_port, int block)
 3 {
 4   /* 创建一个TFTP ACK包 */
 5   char packet[TFTP_ACK_PKT_LEN];
 6  
 7   /* 将开始的2个字节设置为功能码 */
 8   SetTftpOpCode(packet, TFTP_ACK);
 9  
10   /* 制定ACK的块号 */
11   SetTftpBlockNumber(packet, block);
12  
13   return SendTftpMessage(upcb, to, to_port, packet, TFTP_ACK_PKT_LEN);
14 }

2.5 、错误包操作

在包传送的过程中,有没有可能出现错误呢?当然是有的,这就需要所谓的错误包操作码。在服务器不能处理读请求或者写请求的情况下。在文件传输的过程中的读和写也会导致传送这种报文,接着停止传输。我们也需要开发构造和传送错误包的函数。

1 /* 构造并向客户端发送一条错误消息 */
 2 static int SendTftpErrorMessage(struct udp_pcb *upcb, const ip_addr_t *to, int to_port, tftp_errorcode err)
 3 {
 4   char buf[512];
 5   int error_len;
 6  
 7   error_len = ConstructTftpErrorMessage(buf, err);
 8  
 9   return SendTftpMessage(upcb, to, to_port, buf, error_len);
10 }

3 TFTP****服务器实现

我们已经实现了UDP服务器,而且也实现了简单的TFTP协议栈,接下来的工作就是在UDP基础上实现TFTP服务器功能。前面我们已经提到过,复杂的服务器应用只是回到函数的功能不一样,所以开发的过程并无区别。

首先我们来实现初始化部分。创建新的UDP控制块。绑定到制定的服务器端口,我们要实现TFTP服务器,而TFTP协议的端口号为69,所以我们将其绑定到该端口。最后注册TFTP服务器的回调函数。

1 /* 初始化TFTP服务器 */
 2 void Tftp_Server_Initialization(void)
 3 {
 4   err_t err;
 5   struct udp_pcb *tftp_server_pcb = NULL;
 6  
 7   /* 生成新的 UDP PCB控制块 */
 8   tftp_server_pcb = udp_new();
 9  
10   /* 判断UDP控制块是否正确生成 */
11   if (NULL == tftp_server_pcb)
12   { 
13     return;
14   }
15  
16   /* 绑定PCB控制块到指定端口 */
17   err = udp_bind(tftp_server_pcb, IP_ADDR_ANY, UDP_TFTP_SERVER_PORT);
18  
19   if (err != ERR_OK)
20   {
21     udp_remove(tftp_server_pcb);
22     return;
23   }
24  
25   /* 注册TFTP服务器处理函数 */
26   udp_recv(tftp_server_pcb, TftpServerCallback, NULL);
27 }

在初始化中注册了回调函数,所以我们还要实现TFTP服务器的回调函数。这部分出于结构清晰的考虑,我们分成两个函数来写。

1 /* TFTP服务器回调函数 */
 2 static void TftpServerCallback(void *arg, struct udp_pcb *upcb, struct pbuf *p,const ip_addr_t *addr, u16_t port)
 3 {
 4   /* 处理新的连接请求 */
 5   ProcessTftpRequest(p, addr, port);
 6  
 7   pbuf_free(p);
 8 }
 9 /* 从每一个来自addr:port的新请求创建一个新的端口来服务响应,并启动响应过程 */
10 static void ProcessTftpRequest(struct pbuf *pkt_buf, const ip_addr_t *addr, u16_t port)
11 {
12   tftp_opcode op = ExtractTftpOpcode(pkt_buf->payload);
13   char FileName[50] = {0};
14   struct udp_pcb *upcb = NULL;
15   err_t err;
16  
17   /* 生成新的UDP PCB控制块 */
18   upcb = udp_new();
19   if (!upcb)
20   {
21     return;
22   }
23  
24   /* 连接 */
25   err = udp_connect(upcb, addr, port);
26   if (err != ERR_OK)
27   { 
28     return;
29   }
30  
31   ExtractTftpFilename(FileName, pkt_buf->payload);
32  
33   switch (op)
34   {
35   case TFTP_RRQ:
36     {
37  
38       TftpReadProcess(upcb, addr, port, FileName);
39       break;
40     }
41   case TFTP_WRQ:
42     {
43       /* 启动TFTP写模式 */
44       TftpWriteProcess(upcb, addr, port, FileName);
45       break;
46     }
47   default:
48     {
49       /* 异常,发送错误消息 */
50       SendTftpErrorMessage(upcb, addr, port, TFTP_ERR_ACCESS_VIOLATION);
51  
52       udp_remove(upcb);
53  
54       break;
55     }
56   }
57 }

在回调函数中,我们实现了对TFTP读请求和写请求的响应,但这足以验证我们想要实现的TFTP服务器的功能。

4 、结论

本篇我们基于LwIP的UDP实现了一个简单的FTP服务器。这个FTP服务器只是实现FTP协议的功能,具体的应用可根据需要添加。我们使用了TFTP客户端工具对这一服务器进行了基本测试,最终结果符合我们的预期。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    12

    文章

    8921

    浏览量

    85028
  • TFTP
    +关注

    关注

    0

    文章

    20

    浏览量

    14310
  • UDP
    UDP
    +关注

    关注

    0

    文章

    319

    浏览量

    33844
  • 文件传输协议

    关注

    0

    文章

    3

    浏览量

    905
收藏 人收藏

    评论

    相关推荐

    STM32物联网之TFTP文件传输

    TFTP协议理解从以上两张图片,我们了解到什么有用信息呢?每一次文件传输,首先需要发起一个请求,根据请求帧的操作码判断是读文件还是写文件
    发表于 12-30 15:44

    嵌入式开发板iTOP-4412实现TFTP文件传输

    文件传输协议) ,是一个基于 UDP 协议实现的用于在客户机和服务器之间进行简单文件传输
    发表于 03-15 15:41

    第38章 TFTP简单文件传输基础知识

    转帖本章节为大家讲解TFTP(TrivialFile Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作。(本章的知识点主要整理自网络)38.1
    发表于 12-22 08:57

    TFTP协议

    Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户端与服务器之间进行简单文件传
    发表于 11-05 09:14

    基于TFTP文件传输协议实现STM32F407局域网内远程网络升级

    版本。在学习本技术前,应以熟悉TCP/IP中UDP协议,面向无连接,可支持同时多设备在线同时TFTP远程升级。BootLoader_TFTP程序下载:APP应用程序:Tftpd64工具
    发表于 01-12 06:29

    tftp文件传输出现远程主机强制关闭了是怎么回事?

    用的是开发板通过网线连着电脑做调试。两个设备间的相互ping都是没有问题。包括TCP和UDP的各种模式都测过,没有问题。开发板的文件系统都已经有了,但是在调tftp文件传输的时候,发现
    发表于 02-23 15:14

    TFTP服务器是什么如何下载文件

    服务,复杂度和开销都很小。 Tftp是什么 tftp是一个传输文件简单协议,它基于
    发表于 12-12 16:06

    TFTP简单文件传送协议

    TFTP简单文件传送协议:T F T P ( Trivial File Transfer Protocol)即简单
    发表于 09-20 17:59 15次下载

    什么是TFTP

    什么是TFTP  英文原义:Trivial File Transfer Protocol 中文释义:简单文件传输协议或零碎文件传输
    发表于 02-23 11:25 1994次阅读

    TFTP协议

    TFTP 是一个传输文件简单协议,它其于UDP 协议
    发表于 03-02 16:03 34次下载

    TCP协议UDP协议的区别和相同点有哪些 一文看懂TCP协议UDP协议的优缺点

    。里面包括很多协议的。UDP只是其中的一个。之所以命名为TCP/IP协议,因为TCP,IP协议是两个很重要的协议,就用他两命名了。 TCP/
    的头像 发表于 05-26 14:35 9420次阅读
    TCP<b class='flag-5'>协议</b>与<b class='flag-5'>UDP</b><b class='flag-5'>协议</b>的区别和相同点有哪些 一文看懂TCP<b class='flag-5'>协议</b>与<b class='flag-5'>UDP</b><b class='flag-5'>协议</b>的优缺点

    Linux下部署TFTP服务

    TFTP是 Trivial File Transfer Protocol 的缩写,即简单文件传输协议,是一个基于 UDP
    的头像 发表于 04-17 14:56 1086次阅读
    Linux下部署<b class='flag-5'>TFTP</b>服务

    rtthread中使用lwip自带的tftp功能传输文件

    TFTP简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行文件传输
    发表于 07-22 14:06 1154次阅读
    rtthread中使用lwip自带的<b class='flag-5'>tftp</b>功能<b class='flag-5'>传输</b><b class='flag-5'>文件</b>

    RT-Thread中使用lwip自带的tftp功能传输文件

    TFTP协议 TFTP简单文件传输协议)是TCP/IP协议
    的头像 发表于 07-24 19:35 1409次阅读
    RT-Thread中使用lwip自带的<b class='flag-5'>tftp</b>功能<b class='flag-5'>传输</b><b class='flag-5'>文件</b>

    FTP、SFTP、TFTP文件传输协议之间的主要区别

    FTP(File Transfer Protocol,文件传输协议)是用于在计算机网络中传输文件的标准协议
    的头像 发表于 11-15 09:04 5922次阅读
    FTP、SFTP、<b class='flag-5'>TFTP</b><b class='flag-5'>文件传输</b><b class='flag-5'>协议</b>之间的主要区别