基于STM32H7实现Fats+ FTP服务器 功能的实现
本次研究目的是基于SD卡内部的文件内容如何导出的问题所作的探索,传统方式是使用usb接口实现U盘功能,这个之前在F4上已经实现,且目前的应用环境中是包含网络的,我就想到了之前linux文件互传使用的FILEZILLA,于是便萌生了在mcu上实现这个功能的想法,调研阶段发现了ftp和tftp两种方式。(参考源码见附件)详细内容大家可以自行参考其他文档,本文做个简单介绍,重点突出选择的原因。
目录
一、前言
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、文档:
更多推荐
所有评论(0)