STM32物联网之TFTP文件传输

  • 感言:专注物联网应用开发,分享物联网技术经验。
  • 软件平台:IAR6.5
  • TCP/IP协议栈:LWIP1.4.1
  • 硬件平台:STM32F103C8T6有线通信板(点击这里可以购买

1、TCP/IP协议栈LWIP

1.1、LWIP认识

LWIP是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈,是Light
Weight (轻型)IP协议,有无操作系统的支持都可以运行。LWIP提供三种API,分别是RAW
API、LWIP API 、BSD API。其中RAW
API把协议栈和应用程序放到一个进程里边,该接口基于函数回调技术来实现的,适合于无操作系统的场合运行,如单片机。本文使用的就是LWIP的RAW API来实现网络层的通信的。

1.2、TFTP在LWIP中的实现

关于LWIP的移植,就不在本文中多讲,读者可以在网上找到众多资料或在另外的专题中再详细讲解,在这里我们专注其应用。在LWIP中实现一个TFTP服务器非常简单,根据RAW API的编程方法,在初始化的时候创建一个UDP PCB(TFTP使用UDP协议通信),且绑定69端口(TFTP默认通信端口),最后指定该UDP PCB的数据接收回调函数即可。

以上的创建TFTP服务器的方法需要在LWIP初始化,并启动网卡后进行:

 LwIP_Config();
    printf("ipaddr:%d.%d.%d.%d\r\n", net_ip[0], net_ip[1], net_ip[2], net_ip[3]);

    tftpd_init();

在tftpd_init函数中创建TFTP服务器:

void tftpd_init(void)
{
  err_t err;
  unsigned port = 69;

  /* create a new UDP PCB structure  */
  UDPpcb = udp_new();
  if (!UDPpcb)
  {  /* Error creating PCB. Out of Memory  */
    return;
  }

  /* Bind this PCB to port 69  */
  err = udp_bind(UDPpcb, IP_ADDR_ANY, port);
  if (err != ERR_OK)
  {    /* Unable to bind to port  */
    return;
  }

  /* TFTP server start  */
  udp_recv(UDPpcb, recv_callback_tftp, NULL);
}

OK,到这里就完成了TFTP服务器在LWIP中建立起来了,接下来的主要事情就是根据TFTP协议进行协议解释、数据处理。

2、TFTP协议分析

2.1、TFTP通信基本流程(摘自网络)

2.2、TFTP报文格式(摘自网络)

2.3、TFTP协议理解

从以上两张图片,我们了解到什么有用信息呢?

  • 每一次文件传输,首先需要发起一个请求,根据请求帧的操作码判断是读文件还是写文件。
  • 每一帧都有一个操作码用来标识读写。
  • 数据包的长度有一个块编号用来表示数据包的顺序。
  • 数据包中的数据长度为512个字节(在后面的软件我们可以了解到这个长度是可以设定的)。

3、实现TFTP文件传输

3.1、文件传输协议实现

有了第2节的协议分析,我们基本了解了TFTP通信的协议,在这里,我们来实现TFTP的服务器端代码。

在监听的回调函数被触发调用时,首先从请求帧中获取操作码:

typedef enum {
  TFTP_RRQ = 1,
  TFTP_WRQ = 2,
  TFTP_DATA = 3,
  TFTP_ACK = 4,
  TFTP_ERROR = 5
} tftp_opcode;

tftp_opcode tftp_decode_op(char *buf)
{
  return (tftp_opcode)(buf[1]);
}

根据操作码进行相应的处理:

tftp_opcode op = tftp_decode_op(pkt_buf->payload);

switch (op)
  {

    case TFTP_RRQ:    /* TFTP RRQ (read request) */
      tftp_extract_filename(FileName, pkt_buf->payload);
      tftp_process_read(upcb, addr, port, FileName);
      break;

    case TFTP_WRQ:    /* TFTP WRQ (write request) */
      tftp_extract_filename(FileName, pkt_buf->payload);
      //在这个加入擦FALSH
       tftp_process_write(upcb, addr, port, FileName);
      break;

    default:
      /* sEndTransfera generic access violation message */
      tftp_send_error_message(upcb, addr, port, TFTP_ERR_ACCESS_VIOLATION);
      /* TFTP unknown request op */
      /* no need to use tftp_cleanup_wr because no "tftp_connection_args" struct has been malloc'd   */
      udp_remove(upcb);

      break;
  }

这里当STM32接收到写操作请求时,通过tftp_extract_filename函数把文件名读出来。接下来通过tftp_process_write函数来完成文件数据的传输:

int tftp_process_write(struct udp_pcb *upcb, struct ip_addr *to, int to_port, char *FileName)
{
  ... ...
  udp_recv(upcb, wrq_recv_callback, args);
  tftp_send_ack_packet(upcb, to, to_port, args->block);

  return 0;
}

设定数据传输回调函数后,根据TFTP协议,回复一个ACK,之后TFTP客户端开始传输文件数据,从而触发调用wrq_recv_callback

void wrq_recv_callback(void *_args, struct udp_pcb *upcb, struct pbuf *pkt_buf, struct ip_addr *addr, u16_t port)
{
  tftp_connection_args *args = (tftp_connection_args *)_args;
  int n = 0;

  if (pkt_buf->len != pkt_buf->tot_len)
  {
    return;
  }

  /* Does this packet have any valid data to write? */
  if ((pkt_buf->len > TFTP_DATA_PKT_HDR_LEN) &&
      (tftp_extract_block(pkt_buf->payload) == (args->block + 1)))
  {
    /* 在这里处理接收到的数据pkt_buf->payload */

    /* update our block number to match the block number just received */
    args->block++;
    /* update total bytes  */
    (args->tot_bytes) += (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);

    /* This is a valid pkt but it has no data.  This would occur if the file being
       written is an exact multiple of 512 bytes.  In this case, the args->block
       value must still be updated, but we can skip everything else.    */
  }
  else if (tftp_extract_block(pkt_buf->payload) == (args->block + 1))
  {
    /* update our block number to match the block number just received  */
    args->block++;
  }

  /* SEndTransferthe appropriate ACK pkt (the block number sent in the ACK pkt echoes
   * the block number of the DATA pkt we just received - see RFC1350)
   * NOTE!: If the DATA pkt we received did not have the appropriate block
   * number, then the args->block (our block number) is never updated and
   * we simply sEndTransfera "duplicate ACK" which has the same block number as the
   * last ACK pkt we sent.  This lets the host know that we are still waiting
   * on block number args->block+1. */
  tftp_send_ack_packet(upcb, addr, port, args->block);

  /* If the last write returned less than the maximum TFTP data pkt length,
   * then we've received the whole file and so we can quit (this is how TFTP
   * signals the EndTransferof a transfer!)
   */
  if (pkt_buf->len < TFTP_DATA_PKT_LEN_MAX)
  {
    tftp_cleanup_wr(upcb, args);
    pbuf_free(pkt_buf);
  }
  else
  {
    pbuf_free(pkt_buf);
    return;
  }

}

OK!至此STM32就完成了整个TFTP协议文件的接收。

3.2、保存文件数据

接收到完整的文件数据之后,我们需要把数据写到STM32的FLASH中,保存起来。

由于STM32内存较小,不可能开辟一个大的内存空间把文件数据保存起来再写到FLASH,

所以需要边接收边写FLASH。

首先在接收到写操作请求后,把存储区域的FLASH擦除:

    case TFTP_WRQ:    /* TFTP WRQ (write request) */
。    ... ...
      FlashDestination = HtmlDataAddress;
	  /* Erase the needed pages where the user application will be loaded */
      /* Define the number of page to be erased */
      NbrOfPage = FLASH_PagesMask(HtmlTotalSize);//擦除HTML区域

      /* Erase the FLASH pages */
      FLASH_Unlock();
      for (EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
       {
         FLASHStatus = FLASH_ErasePage(HtmlSizeAddress + (PageSize * EraseCounter));
       }
      FLASH_Lock();

在文件数据传输过程中,把<=512BYTE的数据写到FLASH:

     filedata = (uint32_t)pkt_buf->payload + TFTP_DATA_PKT_HDR_LEN;
     FLASH_Unlock();
     for (n = 0;n < (pkt_buf->len - TFTP_DATA_PKT_HDR_LEN);n += 4)
     {
      /* Program the data received into STM32F10x Flash */
      FLASH_ProgramWord(FlashDestination, *(uint32_t*)filedata);

       if (*(uint32_t*)FlashDestination != *(uint32_t*)filedata)
        {
          /* End session */
	 tftp_send_error_message(upcb, addr, port, FLASH_VERIFICATION_FAILED);
         /* close the connection */
         tftp_cleanup_wr(upcb, args); /* close the connection */
        }
        FlashDestination += 4;
        filedata += 4;
     }
     FLASH_Lock();

到这里,就实现了STM32接收TFTP客户端传输的文件数据,并保存到FLASH地址为FlashDestination的区域中。

3.3、演示操作

将通信板连接到与电脑在同一局域的路由器,并正确配置好IP信息。在电脑端打开软件Tftpd32.exe:

点击“上传”按键,就会把文件html.bin文件发送到STM32通信板:

可以在STM32通信板中把文件内容读出来使用,在下一篇博客物联网WEB开发中会使用TFTP传输HTML文件。

4、TFTP的应用

TFTP主要是实现文件传输,在固件升级、程序调试中极大提高效率,有重要的意义。

在WEB的应用开发中会体会到其强大的作用。欢迎关注下一篇关于STM32的WEB开发的博客。

时间: 2024-08-30 05:39:29

STM32物联网之TFTP文件传输的相关文章

Tftp文件传输服务器(基于UDP协议)

一个简单的UDP服务端与客户端 服务端: 1 from socket import * 2 #创建套接字 3 udp_server = socket(AF_INET,SOCK_DGRAM) 4 5 msg_server = ("",8877) 6 #绑定ip地址和端口 7 udp_server.bind(msg_server) 8 9 while True: 10 #接受消息,注意此处用的是 recvfrom() 11 msg_client = udp_server.recvfrom(

基于TCP的TFTP(Trivial File Transfer Protocol,简单文件传输协议) 的c编程实现

我们或许都听到过,TFTP(Trivial File Transfer Protocol,简单文件传输协议)是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂.开销不大的文件传输服务. 本文就简单的叙述下tftp的小文件传输功能以及客户端对服务器的列表功能. 之前就一直很纳闷,我们经常在网上下载什么东西或者从别处传输一个文件,具体是怎么实现的呢?于是乎,翻查一些资料,加上自己对网络编程的逐步加深,所以功夫不负有心人,还算是大致的完成了下. 本例程实现的功能呢?

tftp简单文件传输协议搭建

TFTP 简单文件传输协议 安装 sudo apt-get install tftp  tftpd openbsd-inetd 需要tftp tftpd openbsd-inetd  3个应用组件 配置 在/etc/inetd.conf tftp        dgram    udp    wait    nobody    /usr/sbin/tcpd    /usr/sbin/in.tftpd /home/gec/tftp 其中/home/gec/tftp就是tftp通信目录 建立目录 和

文件传输协议(FTP、TFTP)

FTP <1>FTP协议是互联网上广泛使用的文件传输协议. <2>客户端/服务器模式基于TCP(Transmission Control Protocol 传输控制协议,面向连接的.可靠的.基于字节流的传输层通信协议) <3>FTP采用双TCP连接方式 控制连接使用TCP端口号21(控制连接在整个FTP回话期间一直保持打开) 数据连接使用TCP端口号20(数据上传.下载.文件列表发送等.数据传输结束后数据连接将终止) <4>FTP有两种文件传输模式 ASCI

TFTP 简单文件传输协议

简 介 简单文件传输协议是一种基于UDP协议的客户端和服务器之间进行简单文件传输的协议,它提供了不复杂.开销不大的文件传输服务. 它使用UDP协议的69号端口作为其传输,不能列出目录内容,无验证或加密机制,被用于在远程服务器上读取或写入文件,因此文件的传输过程也不像FTP协议那样可靠.但是TFTP不需要客户端的权限认证,也就减少了无谓的系统和网络带宽消耗,因此在传输琐碎不大的文件时,效率更加高,目前主要适用于私人的本地网络中,常用于PXE无盘启动,网络设备的设置等. 部署环境: rhel6.5操

【RL-TCPnet网络教程】第38章 TFTP简单文件传输基础知识

第38章      TFTP简单文件传输基础知识 本章节为大家讲解TFTP(Trivial File Transfer Protocol,简单文件传输协议)的基础知识,方便后面章节的实战操作. (本章的知识点主要整理自网络) 38.1  初学者重要提示 38.2  TFTP基础知识参考资料 38.3  TFTP基础知识点 38.4  总结 38.1  初学者重要提示 TFTP简单文件传输协议在实际项目中有比较重要的实用价值,需要初学者对TFTP的基础知识也有个认识. 38.2  TFTP基础知识

Linux下几种文件传输命令

Linux下几种文件传输命令 sz rz sftp scp 最近在部署系统时接触了一些文件传输命令,分别做一下简单记录: 1.sftp Secure Ftp 是一个基于SSH安全协议的文件传输管理工具.由于它是基于SSH的,会在传输过程中对用户的密码.数据等敏感信息进行加密,因此可以有效的防止用户信息在传输的过程中被窃取,比FTP有更高的安全性.在功能方面与FTP很类似,不仅可以传输文件数据,而且可以进行远程的文件管理(如建立,删除,查看文件列表等操作).Sftp与ftp虽然只有一字之差,但基于

嵌入式系统下文件传输实验

TCP网络通信编程,多线程(代码见附录) 实验目的: 实现PC与物联网试验箱间传输文件,PC作为客户端,在PC上输入一个文件名,通过编制的程序上传到物    联网试验箱上.传输协议采用TCP协议.在PC的linux系统上运行client,在物联网试验箱的linux系统上运    行server.实验步骤:   (1)在PC端打开虚拟机(unbuntu系统),将实验板接上电源并使用网线将开发板与PC相连.   (2)使用命令ifconfig eth0 192.168.1.21设置PC端IP地址, 

Linux的文件传输命令总结

因为工作原因,需要经常在不同的服务器见进行文件传输,特别是大文件的传输,因此对linux下不同服务器间数据传输命令和工具进行了研究和总结.主要是rcp,scp,rsync,ftp,sftp,lftp,wget,curl. rcp rcp不是一种安全的的传输文件的方式,rcp通过rsh(rsh见下面)来执行远程命令,要使用rcp必须经过一些配置,现在rcp已经被scp取代了,常用scp来进行文件传输.要使用rcp,需要具备以下条件: (1)如果系统中有/etc/hosts 文件,应确保该文件包含要