目录

一、前言

1、起源:

2、FTP/TFTP介绍

(1 )FTP介绍

(2)TFTP介绍

(3)选择原因

二、实现流程

1、基础知识构建

2、移植源码到自己的工程中

3、代码调试

(1)SOCKET问题:

(2)filezilla的设置:

(3) 文件上传

(4)主动模式和被动模式

(5)文件夹无法上传

三、结论及测试结果


一、前言

1、目的:

      本次研究目的是基于SD卡内部的文件内容如何导出的问题所作的探索,传统方式是使用usb接口实现U盘功能,这个之前在F4上已经实现,且目前的应用环境中是包含网络的,我就想到了之前linux文件互传使用的FILEZILLA,于是便萌生了在mcu上实现这个功能的想法,调研阶段发现了ftp和tftp两种方式。(参考源码见附件)

2、FTP/TFTP介绍

        详细内容大家可以自行参考其他文档,本文做个简单介绍,重点突出选择的原因。

(1 )FTP介绍

       FTP是用于在网络上进行文件传输的一套标准协议,它工作在 OSI 模型的第七层, TCP 模型的第四层, 即应用层,提供一种在服务器和客户机之间上传和下载文件的有效方式。

  • 基于TCP的传输
  • FTP采用双TCP连接方式
  • 多用于Windows操作系统系统
  • 支持授权与认证机制,提供目录列表功能
  • FTP协议使用TCP端口中的 20和21这两个端口,其中20用于传输数据,21用于传输控制信息

(2)TFTP介绍

      Trivial File Transfer Protocol,简单文件传输协议,一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。端口号为69。

  • TFTP封装:Ethernet 2/IPv4/UDP/FCS
  • TFTP适用于客户端和服务器之间不需要复杂交互的环境
  • TFTP仅提供简单的文件传输功能(上传、下载)
  • TFTP不提供存取授权与认证机制,不提供目录列表功能
  • TFTP协议传输是由客户端发起的

(3)选择原因

      相较于FTP,TFTP的设计就是以传输小文件为目标,协议实现简单很多。TFTP是一个传输文件的简单协议,它基于UDP协议而实现。它不具备通常的FTP的许多功能,它只能从文件服务器上获得或写入文件,不能列出目录,不进行认证,它传输8位数据。TFTP代码所占的内存较小,广泛应用于没有硬盘的嵌入式设备。 TFTP在lwip的协议栈中已经实现了。

总结: TFTP相对简单:使用简单、实现简单,处理器要求低,但是不能知道对方有那些文件,必须要提前确定好文件名才能进行传输,比较适合明确性的某个文件数据类的传输,不能用作文件的查看和管理类功能。 TFTP相对的优势就是fatfs内部所有的文件结构一览无余,和操作自己的电脑U盘一样,上传,下载、删除、新建等功能均可以。

考虑到本项目是想实现U盘一样的功能,外加H7处理器足够强大,故通过FTP实现。

二、实现流程

1、基础知识构建

(1)先了解下ftp实现的原理及mcu上实现服务器的可能性,然后下载ftp服务器源码(网上没看到标准开源的码,我是从别人工程中下载的。rt-thread官方有个支持包,但不提供源码,只提供lib,放弃),下载后源码如下:

(2)下载filezilla安装程序,安装比较简单,安装完成之后如下图:

2、移植源码到自己的工程中

该源码是基于socket+rtos编程的,所以要注意自己的lwip已经能满足这个条件了。

移植基本上没什么太大的改动就可以编译通过了。

3、代码调试

由于别人工程的可靠性未知,代码还是需要大量的调试,调试的过程中也同步了解了ftp的传输流程及原理,这里记录一下过程中几个问题及原理以供大家参考:

(1)SOCKET问题:

   ftp需要消耗较多的socket资源,所以在前期调试阶段一定要将lwip的资源设置好。由于操作的过程中的不停的释放和打开socket,该源码在处理关闭时有一些地方不妥,导致使用过程中socket资源不足而出现问题,所以一定要处理好关闭和打开。

(2)filezilla的设置:

设置原因也是因为socket资源和主动模式被动模式两个原因,mcu用户可以设置如下:

(3) 文件上传

贴出这个打印信息,方便大家了解流程

[13:51:15.627]收←◆listen cmd :new sock: 4, connection from 192.168.3.44
a new session created
inser sock 4
inser pos 1
listen cmd :new sock: 5, connection from 192.168.3.44
a new session created
inser sock 5
inser pos 2
192.168.3.44 requested "AUTH TLS"
...current session sock 5....
-------------------502 Not Implemented yet
192.168.3.44 requested "AUTH TLS"
...current session sock 4....
-------------------502 Not Implemented yet
192.168.3.44 requested "AUTH SSL"
...current session sock 5....
-------------------502 Not Implemented yet
192.168.3.44 requested "AUTH SSL"
...current session sock 4....
-------------------502 Not Implemented yet
192.168.3.44 requested "USER ftcs"
...current session sock 5....
-------------------CMD_USER
192.168.3.44 requested "USER ftcs"
...current session sock 4....
-------------------CMD_USER
192.168.3.44 requested "PASS ftcs"
...current session sock 5....
-------------------CMD_PASS
192.168.3.44 requested "PASS ftcs"
...current session sock 4....
-------------------CMD_PASS
192.168.3.44 requested "CWD /"
...current session sock 5....
-------------------CMD_CWD
CWD: Changed to directory /
192.168.3.44 requested "PWD"
...current session sock 5....
-------------------CMD_PWD
192.168.3.44 requested "CWD /"
...current session sock 4....
-------------------CMD_CWD
CWD: Changed to directory /
192.168.3.44 requested "TYPE A"
...current session sock 5....
-------------------CMD_TYPE
192.168.3.44 requested "PORT 192,168,3,44,196,147"
...current session sock 5....
-------------------CMD_PORT
rm_sock 544501615
set_ftpd_manage_pasv_sockfd 6
ERROR: Bind socket
SUCCESS: Bind socket to 20 port
  active Connected to Data(PORT) 192.168.3.44 @ 50323
192.168.3.44 requested "PWD"
...current session sock 4....
-------------------CMD_PWD
192.168.3.44 requested "RETR config.txt"
...current session sock 5....
-------------------CMD_RETR
file name is config.txt
get filesize 4 bytes
150 Opening binary mode data connection for "config.txt" (4 bytes
[13:51:15.864]收←◆).
rm_sock 6
rm_pasv_sockfd

[13:51:15.939]收←◆192.168.3.44 requested "PORT 192,168,3,44,196,148"
...current session sock 5....
-------------------CMD_PORT
rm_sock 6
set_ftpd_manage_pasv_sockfd 6
ERROR: Bind socket
SU
[13:51:15.973]收←◆CCESS: Bind socket to 20 port
  active Connected to Data(PORT) 192.168.3.44 @ 50324
192.168.3.44 requested "RETR f006.txt"
...current session sock 5....
-------------------CMD_RETR
file name is f006.txt
get filesize 1094 bytes
150 Opening binary mode data connection for "f006.txt" (1094 bytes).
rm_sock 6
rm_pasv_sockfd

[13:51:16.073]收←◆192.168.3.44 requested "PORT 192,168,3,44,196,149"
...current session sock 5....
-------------------CMD_PORT
rm_sock 6
set_ftpd_manage_pasv_sockfd 6
ERROR: Bind socket
SUCCESS: Bind socket to 20 port

[13:51:16.115]收←◆  active Connected to Data(PORT) 192.168.3.44 @ 50325
192.168.3.44 requested "RETR plt_msg1476056.txt"
...current session sock 5....
-------------------CMD_RETR
file name is plt_msg1476056.txt
get filesize 0 bytes
150 Opening binary mode data connection for "plt_msg1476056.txt" (0 bytes).
rm_sock 6
rm_pasv_sockfd

[13:51:16.188]收←◆192.168.3.44 requested "PORT 192,168,3,44,196,150"
...current session sock 5....
-------------------CMD_PORT
rm_sock 6
set_ftpd_manage_pasv_sockfd 6
ERROR: Bind socket
SUCCESS: Bind socket to 20 port
  active Connected to Data(PORT) 192.168.3.44 @ 50326
192.168.3.44 requested "RETR plt_msg1476057.txt"
...current session sock 5
[13:51:16.240]收←◆....
-------------------CMD_RETR
file name is plt_msg1476057.txt
get filesize 40270 bytes
150 Opening binary mode data connection for "plt_msg1476057.txt" (40270 bytes).

[13:51:16.326]收←◆rm_sock 6
rm_pasv_sockfd

[13:51:17.729]收←◆192
[13:51:17.756]收←◆.168.3.44 requested "TYPE I"
...current session sock 4....
-------------------CMD_TYPE
192.168.3.44 requested "PORT 192,168,3,44,196,156"
...current session sock 4....
-------------------CMD_PORT
rm_sock 589505315
set_ftpd_manage_pasv_sockfd 6
SUCCESS: Bind socket to 20 port
  active Connected to Data(PORT) 192.168.3.44 @ 50332
192.168.3.44 requested "RETR common_board.ioc"
...current session sock 4....
-------------------CMD_RETR
file name is common_board.ioc
get filesize 18286 bytes
150 Opening binary mode data connection for "common_board.ioc" (18286 bytes).

[13:51:17.837]收←◆rm_sock 6
rm_pasv_sockfd

[13:52:16.363]收←◆Clien
[13:52:16.381]收←◆t 192.168.3.44 disconnected
last session closed
rm_sock 5
rm_client_sockfd:2

[13:52:17.884]收←◆Client 192.168.3.44 disconnected
last session closed
rm_sock 4
rm_client_sockfd:1

(4)主动模式和被动模式

       一开始测试是主动模式和被动模式都打开了,有些问题,后面就没开被动模式了,也许随着其他问题的解决,被动模式也没问题了,但没有再进行测试:

主动模式:
       客户端发送PORT命令,格式为:PORT h1,h2,h3,h4,p1,p2
其中,h1-h4是客户端IP地址的四个字节,p1-p2是客户端端口号的高位和低位字节。
服务器回应:200 PORT command successful.
服务器使用20端口连接到客户端指定的端口。
数据传输开始。
被动模式:
       客户端发送PASV命令,请求服务器进入被动模式。
服务器回应:227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
其中,h1-h4是服务器IP地址的四个字节,p1-p2是服务器端口号的高位和低位字节。
客户端连接到服务器的这个端口。
数据传输开始。

(5)文件夹无法上传

       出现了文件夹上传就是失败,原因是上传文件夹并没有在对应的目录中创建文件夹的目录,需要修改下上传代码命令部分,增加创建目录的步骤:

case CMD_STOR: // 客户端上传数据
		printf("-------------------CMD_STOR\r\n");

		if (f_mkdir(conn->currentdir))
		{
			printf("create dir failed: %s\r\n	", conn->currentdir);
		}
		else
		{
			printf("create dir success: %s\r\n	", conn->currentdir);
		}
		do_full_path(conn, parameter_ptr, spare_buf, 256);
		/* Открываем файло на запись */
		rc = f_open(&file, spare_buf, FA_WRITE | FA_OPEN_ALWAYS);
		if (rc != FR_OK)
		{
			printf("open failed: %s\r\n	", spare_buf);
			do_send_reply(conn->sockfd, "550 Cannot open \"%s\" for writing.\r\n", spare_buf);
			rm_ftpd_manage_sock(conn->pasv_sockfd);
			break;
		}
		do_send_reply(conn->sockfd, "150 Opening binary mode data connection for \"%s\".\r\n", spare_buf);

		FD_ZERO(&readfds);
		FD_SET(conn->pasv_sockfd, &readfds);
		printf("Waiting %d seconds for data...\r\n", tv.tv_sec);
		led_w = 0;

		while (select(conn->pasv_sockfd + 1, &readfds, 0, 0, &tv) > 0)
		{
			if ((num_bytes = recv(conn->pasv_sockfd, sbuf, FTP_BUFFER_SIZE, 0)) > 0)
			{
				unsigned bw;
				f_write(&file, sbuf, num_bytes, &bw);
				if (led_w++ % 50 == 0)
				{
					// led_toggle(LED1);
				}
			}
			else if (num_bytes == 0)
			{
				f_close(&file);
				do_send_reply(conn->sockfd, "226 Finished.\r\n");
				rm_ftpd_manage_sock(conn->pasv_sockfd);
				break;
			}
			else if (num_bytes == -1)
			{
				f_close(&file);
				ret = -1;
				rm_ftpd_manage_sock(conn->pasv_sockfd);
				break;
			}
		}
		rm_ftpd_manage_sock(conn->pasv_sockfd);
		break;

(6)上传大文件问题

      上传大文件时出现错乱,大小不对,原因是fatfs并没有设置支持多线程操作,需要修改配置,可以参考下面这篇文档:

STM32-RT-thread FATFS文件系统添加多线程支持,以支持同时读写多个文件-CSDN博客

(7) 传输速度

        使用一个大文件进行速度测试,发现屏蔽了fatfs写入速度也只能到2MB/S,正常写入不带多线程写入功能1.2MB/S,带多线程写入功能反而能到1.5MB/S,下载速度能到1.8MB/S。

       猜测可能是filezilla客户端速度只能到这么多,有知道原因麻烦评论下。(fatfs的读写速度测试能到10MB/S以上).https://zhuanlan.zhihu.com/p/387363606

需要修改参数 FTP_BUFFER_SIZE以提高速度,不然更慢

三、结论及测试结果

(1)支持文件上传,支持文件夹上传(但里面不能再包含一级了),速度最高到1.8MB/S

(2)支持文件下载,支持文件夹下载,速度最高到1.5MB/S

(3)支持在线编辑文件和保存操作

(4)支持在线增加文件夹和文件,不支持更改文件名

(5)支持删除文件及文件夹

(6)支持大文件传输,测试了一个300M的文件

(7)目录偶尔需要手动刷新一下

      最终调试完成后可以流畅实现文件自由的上传和下载,多个文件、单个文件、文件夹的上传和下载等等均是可以,且没有出现任何红色字体提示失败,如下图所示:

四、参考资料

1、源码

https://download.csdn.net/download/u010715044/90119331?spm=1001.2101.3001.9499

2、文档:

FTP详解(持续更新)_ftp命令行详解-CSDN博客

tcp服务器设置accpet为非阻塞的两种处理方式_tcp accept 非阻塞-CSDN博客

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐