背景:
- 最近有个项目在stm32里面使用ftp上传文件到服务器,项目做完,做点笔记,公司的嵌入式设备使用了CH395Q进行有线联网,创建了一个TCP通信,用于协议通信,没有其他方式联网,客户有日志上传的需求,日志是MCU通过FatFS文件协议记录在U盘的CSV格式文件,这个文件数据少的有几百k,多的可能上M,不能直接使用协议通信传输,目前已知的文件服务器有http和ftp,http多用于下载,并且是短连接的,无法主动客户端发起,只能服务器发起文件传输,相比于http,ftp可以在有数据传输需求的时候,发送登录连接,传输过程中传输socket是不用断开的,速度比较快,可以满足功能需求。实现方面,网上也有很多现成的ftp库可以参考,理论可行,以下是记录了我整个从移植调试过程。
过程:
-
首先下载一个ftp库,https://www.mbpfaus.net/~pfau/ftplib/

最新的是ftplib4.0-1,直接下载压缩包即可。看英语应该也知道,这个是Linux的lib库,我目标是在stm32上使用,为什么不找stm32的库呢,因为我没有找到,将就着用吧,看一下源码也知道,整个包就三个文件有用,ftplib.c,ftplib.h,qftp.c,把着三个文件单独摘出来放到STM32项目里面,eide工程里面添加了一个ftpclient文件夹放这三个文件,改一改代码跑起来应该就行了。
-
接下来是改代码:
-
1.因为编译器问题,首先需要删除Linux,window特殊接口和头文件,按需增加stm32支持的头文件。比如:
c
// shutdown函数(CH395Q不支持,使用关闭socket代替)
int shutdown(uint8_t sock, int how) {
return CH395CloseSocket(sock);
}
// unlink函数(文件系统相关,在嵌入式系统中通常不需要)
int unlink(const char *pathname) {
return 0; // 返回成功
}
- 2.库里面的TCP相关的接口,适配CH395Q的驱动代码,增加ftp相关的socket定义,CH395Q已经使用了0索引用于TCP应用协议,我增加了1索引用于ftp控制socket,2索引用于ftp数据传输。控制socket的结构主要是
c
struct NetBuf {
char *cput,*cget;
int handle;
int cavail,cleft;
char *buf;
int dir;
netbuf *ctrl;
netbuf *data;
int cmode;
struct tm idletime;
FtpCallback idlecb;
void *idlearg;
unsigned long int xfered;
unsigned long int cbbytes;
unsigned long int xfered1;
char response[RESPONSE_BUFSIZ];
};
- 3.库里面文件操作的改为FatFS的文件接口,比如fd_set 改为FIL。
4.修改qftp.c 增加ftp客户端调用接口,传入ftp连接,就可以连接登录上ftp服务器并且把本地的csv文件上传,ftp服务器部分的代码本着能编译通过的方式去做,如果影响编译直接删除了。使用PASV模式,传输模式是二进制,提高兼容性。
改完编译通过,终于可以开始调试了。
- 第一步来了一个下马威,TCP连接失败,检查代码发现socket索引问题,端口也是默认端口,没有使用真实的ftp端口,完善了ftplib.c的FtpConnect,FtpQuit,FtpSendCmd函数。还有库使用的是动态内存,内存不太够,直接分配了一个静态内存解决了。整个ftplib.c的关键变量是netbuf
*nControl。 - 第二步可以连上了,但登录失败,后面发现是socket处理代码逻辑bug。
- 第三步登录了,服务器看到的文件是空白。
因为设计问题(ftp控制放在了CH395Q的控制代码里面),FtpSendCmd之前需要ch395_socket_tcp_client_interrupt(nControl->handle);//一定要读取中断,否则不能发送
否则即使发送成功了,也会格式错乱不完整。 - 第四步数据传输过程中,系统复位了,应该是发送数据的过程中,看门狗没有人喂乱咬人了,就在每一帧数据发送的间隙喂狗就好了,整个时候发送文件优先。
- 第五步可以正常传输了,文件越大传输越久,我测试的时候16k,实际场景几百,整个受限于内存,传输socket缓存空间,修改缓存空间大小有改善。嵌入式资源也比较紧张,根据实际条件调节即可
。
我是电脑搭建的ftp服务器,调试过程中可以通过抓包工具wireshark工具分析ftp的控制socket过程,也蛮有意思的。

看原始包就知道是客户端没有数据,还是服务器没有数据,再使用流追踪就可以清楚的看到ftp连接上后的欢迎信息,客户端登录过程,传输前设置的传输格式,传输模式,发送的文件和传输结果。
