
FTP命令端口21 数据端口2100
(没有读卡器 o(╥﹏╥)o)
在Filezila里面 一直读不到目录,仔细检查LIST_Commond函数也没有问题;
原先使用Filezila随意拖动了一些文件进去,得不到响应,
后来才想起来这种最原始的调试方法:
很神奇的是每次断开2100端口连接,再点击连接,数据端口的信息就打印出来了
[2023-04-22 23:30:45.897]# The server is connected from local 192.168.0.4:10202
[2023-04-22 23:30:45.966]# RECV ASCII>
0-0-1980 0:0:0 154394 SWUST.jpg
0-0-1980 0:0:0 <DIR> CYDZ
0-0-1980 0:0:0 <DIR> dir
0-0-1980 0:0:0 452 TESTCS~1.USE
0-0-1980 0:0:0 <DIR> a
0-0-1980 0:0:0 5198 TEST~1.CSP
0-0-1980 0:0:0 11627 FORMTE~1.CS
0-0-1980 0:0:0 37825 FORMTEST.CS
0-0-1980 0:0:0 6211 FORMTE~1.RES
0-0-1980 0:0:0 2864 RESOUR~1.CS
0-0-1980 0:0:0 5612 RESOUR~1.RES
0-0-1980 0:0:0 1089 SETTIN~1.CS
0-0-1980 0:0:0 4286 swust.ico
[2023-04-22 23:30:45.991]# The server is disconnected and a reconnection is scheduled in 5 seconds.
[2023-04-22 23:30:50.987]# Reconnecting to the server...
[2023-04-22 23:30:51.001]# The server is connected from local 192.168.0.4:10203
这应该是FTP中DOS类型的数据格式
命令端口
LIST /
[2023-04-22 23:28:27.169]# RECV ASCII>
150 Here comes the directory listing.
[2023-04-22 23:28:28.595]# RECV ASCII>
226 Transfer complete. Closing data connection
[2023-04-22 23:29:23.765]# SEND ASCII>
LIST /CYDZ
[2023-04-22 23:29:23.774]# RECV ASCII>
150 Here comes the directory listing.
[2023-04-22 23:29:25.552]# RECV ASCII>
226 Transfer complete. Closing data connection
[2023-04-22 23:29:31.121]# SEND ASCII>
LIST /CYDZ
[2023-04-22 23:29:31.128]# RECV ASCII>
150 Here comes the directory listing.
[2023-04-22 23:29:33.532]# RECV ASCII>
226 Transfer complete. Closing data connection
[2023-04-22 23:29:49.618]# SEND ASCII>
CWD CYDZ
[2023-04-22 23:29:49.633]# RECV ASCII>
250 Directory changed to /CYDZ
[2023-04-22 23:30:00.787]# SEND ASCII>
pwd
[2023-04-22 23:30:00.793]# RECV ASCII>
257 "/CYDZ " is your current location
[2023-04-22 23:30:07.665]# SEND ASCII>
LIST
[2023-04-22 23:30:07.674]# RECV ASCII>
150 Here comes the directory listing.
[2023-04-22 23:30:09.366]# RECV ASCII>
226 Transfer complete. Closing data connection
[2023-04-22 23:30:35.002]# SEND ASCII>
CDUP
[2023-04-22 23:30:35.019]# RECV ASCII>
250 Directory changed to /
[2023-04-22 23:30:43.991]# SEND ASCII>
LIST
[2023-04-22 23:30:44.007]# RECV ASCII>
150 Here comes the directory listing.
[2023-04-22 23:30:45.966]# RECV ASCII>
226 Transfer complete. Closing data connection
命令端口的LIST、CDUP、pwd、CWD都没有问题
这是list命令解析函数
int16_t cmd_LIST(char *outbuffer)
{
if (outbuffer) {
#if TCP_SERVICE
#if !FTP_ANONYMOUS
if (!tcpsrv_status.loginOK)
return cmd_530(outbuffer);
#endif
char buffer[MAX_PATH];
int ret;
FRESULT fr;
ret = ftpd_fullpath(buffer, MAX_PATH, NULL, NULL);
if (ret == -1)
goto listerr;
fr = f_opendir(&dir, buffer);
if (fr != FR_OK)
goto listerr;
cwdir_ptr = &dir; // 文件夹打开了之后才能赋给state->dp
// ret = ftpd_prepare_data(state);
// if (ret == -1)
// goto listerr2;
strcpy(outbuffer,"150 Here comes the directory listing.\r\n");
tcpsrv_status.data_state = 1; // :1 is list info the data channel take some action.
tcpsrv_status.LISTcontinue = 0; // reset Listing :ready to list in datachannel
return strlen(outbuffer);
listerr:
sprintf(outbuffer,"550 Failed to open file.\r\n");
return strlen(outbuffer);
#else
return cmd_502(outbuffer); //模式未实现
#endif
}
else
{
return 0;
}
}
遍历读取文件目录
int16_t cmd_MMCdir(char *outbuffer)
{
#if USE_MMC
if (!cwdir_ptr)
return 0;
#if !FTP_ANONYMOUS
if (outbuffer && !tcpsrv_status.loginOK)
return cmd_530(outbuffer);
#endif
#if TCP_SERVICE
tcpsrv_status.LISTcontinue = 0; // continue Flag zur點ksetzen
#endif
if (!outbuffer) {
//usart_write("\n\rSD-Karte:");
}
char *pstr = outbuffer;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
while(f_readdir(cwdir_ptr, &fileinfo) == FR_OK) //f_read is ok
{
if(fileinfo.fname[0] == '\0')
{
break;
}
if (strcmp(fileinfo.fname, ".") == 0 || strcmp(fileinfo.fname, "..") == 0) //.和..是本级可以跳过
{
continue;
}
fat16_get_file_modification_date(&fileinfo, &year, &month, &day);
fat16_get_file_modification_time(&fileinfo, &hour, &min, &sec);
if (outbuffer) {
#if TCP_SERVICE
char tmpbuf[12];
#ifdef UNIX_LIST
/*
* UNIX style Dateiliste
*/
char mstr[4];
if (dir_entry.fattrib & AM_DIR) {
strcpy((char *)tmpbuf,("drwxr-xr-x"));
}
else {
strcpy((char *)tmpbuf,("-rw-rw-rw-"));
}
strncpy(mstr,&US_Monate[(month-1)*3],3);
mstr[3] = '\0';
sprintf((char *)pstr,("%s 1 ftp ftp %ld %s/%i/%i %i:%i %s\r\n"),tmpbuf, fileinfo.file_size,
mstr, day, year, hour, min, fileinfo.lfname);
#endif
#ifdef DOS_LIST
/*
* DOS style Dateiliste
* (bei FileZilla nennt sich das DOS - woanders Windows_NT(?) ...)
*/
if (fileinfo.fattrib & AM_DIR) {
strcpy((char *)tmpbuf,("<DIR>"));
}
else {
sprintf(tmpbuf,("%ld"),fileinfo.fsize);
}
sprintf((char *)pstr,("%i-%i-%i %i:%i:%i %8s %s\r\n"),month, day, year, hour, min, sec, tmpbuf,fileinfo.fname);
#endif
while (*pstr++); // 把结束符消除掉
--pstr;
if (pstr > (outbuffer + MAX_WINDOWS_SIZE - 64)) { // 大于滑动窗口
tcpsrv_status.LISTcontinue = 1; // continue Flag setzen
break;
}
#endif
}
else {
// usart_write("\r\n%2i.%2i.%i %2i:%2i ",day, month, year, hour, min);
// if (fileinfo.fattrib & AM_DIR)
// usart_write(" <DIR> ");
// else
// usart_write("%9l ",fileinfo.fsize);
// usart_write("%s",fileinfo.lfname);
}
}
#if TCP_SERVICE
if (!tcpsrv_status.LISTcontinue)
f_rewinddir(cwdir_ptr);
#endif
if (outbuffer)
return strlen(outbuffer);
else
#endif
return 0;
}
命令格式
1.LIST
- DOS类型
DOS格式共分四段,其中第一段为日期,第二段为时间,第三段为文件长度,第四段为文件名称。
m-d-y[space]h-m-s[space][fisesize/<DIR][space][filename]
0-0-1980 0:0:0 11627 FORMTE~1.CS
0-0-1980 0:0:0 37825 FORMTEST.CS
0-0-1980 0:0:0 6211 FORMTE~1.RES
0-0-1980 0:0:0 2864 RESOUR~1.CS
0-0-1980 0:0:0 5612 RESOUR~1.RES
0-0-1980 0:0:0 1089 SETTIN~1.CS
0-0-1980 0:0:0 4286 swust.ico
代码如下:
char US_Monate[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
int16_t cmd_MMCdir(char *outbuffer)
{
#if USE_MMC
if (!cwdir_ptr)
return 0;
#if !FTP_ANONYMOUS
if (outbuffer && !tcpsrv_status.loginOK)
return cmd_530(outbuffer);
#endif
#if TCP_SERVICE
tcpsrv_status.LISTcontinue = 0; // continue Flag zur點ksetzen
#endif
if (!outbuffer) {
//usart_write("\n\rSD-Karte:");
}
char *pstr = outbuffer;
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
/* Verzeichniseintr鋑e lesen */
while(f_readdir(cwdir_ptr, &fileinfo) == FR_OK) //f_read is ok
{
if(fileinfo.fname[0] == '\0')
{
break;
}
if (strcmp(fileinfo.fname, ".") == 0 || strcmp(fileinfo.fname, "..") == 0) //.和..是本级可以跳过
{
continue;
}
fat16_get_file_modification_date(&fileinfo, &year, &month, &day);
fat16_get_file_modification_time(&fileinfo, &hour, &min, &sec);
if (outbuffer) {
#if TCP_SERVICE
char tmpbuf[12];
#ifdef UNIX_LIST
/*
* UNIX style Dateiliste
*/
char mstr[4];
if (fileinfo.fattrib & AM_DIR) {
strcpy((char *)tmpbuf,("drwxr-xr-x"));
}
else {
strcpy((char *)tmpbuf,("-rw-rw-rw-"));
}
strncpy(mstr,&US_Monate[(month-1)*3],3);
mstr[3] = '\0';
sprintf((char *)pstr,"%s 1 ftp ftp %ld %s/%i/%i %i:%i %s\r\n",tmpbuf, fileinfo.fsize,
mstr, day, year, hour, min, fileinfo.fname);
#endif
#ifdef DOS_LIST
/*
* DOS style Dateiliste
* (bei FileZilla nennt sich das DOS - woanders Windows_NT(?) ...)
*/
if (fileinfo.fattrib & AM_DIR) {
strcpy((char *)tmpbuf,("<DIR>"));
}
else {
sprintf(tmpbuf,("%ld"),fileinfo.fsize);
}
sprintf((char *)pstr,("%i-%i-%i %i:%i:%i %8s %s\r\n"),month, day, year, hour, min, sec, tmpbuf,fileinfo.fname);
#endif
while (*pstr++); // 把结束符消除掉
--pstr;
if (pstr > (outbuffer + MAX_WINDOWS_SIZE - 64)) { // 大于滑动窗口
tcpsrv_status.LISTcontinue = 1; // continue Flag setzen
break;
}
#endif
}
else {
// usart_write("\r\n%2i.%2i.%i %2i:%2i ",day, month, year, hour, min);
// if (fileinfo.fattrib & AM_DIR)
// usart_write(" <DIR> ");
// else
// usart_write("%9l ",fileinfo.fsize);
// usart_write("%s",fileinfo.lfname);
}
}
#if TCP_SERVICE
if (!tcpsrv_status.LISTcontinue)
f_rewinddir(cwdir_ptr);
#endif
if (outbuffer)
return strlen(outbuffer);
else
#endif
return 0;
}
- UNIX类型
好消息是,unix格式可以被成功解析,但是格式还是有一些问题:
-rw-rw-rw- 1 ftp ftp 14240 /0/1980 0:0 SWUST.jpg
drwxr-xr-x 1 ftp ftp 0 /0/1980 0:0 CYDZ
drwxr-xr-x 1 ftp ftp 0 /0/1980 0:0 dir
-rw-rw-rw- 1 ftp ftp 452 /0/1980 0:0 TESTCS~1.USE
drwxr-xr-x 1 ftp ftp 0 /0/1980 0:0 a
-rw-rw-rw- 1 ftp ftp 5198 /0/1980 0:0 TEST~1.CSP
-rw-rw-rw- 1 ftp ftp 11627 /0/1980 0:0 FORMTE~1.CS
-rw-rw-rw- 1 ftp ftp 37825 /0/1980 0:0 FORMTEST.CS
-rw-rw-rw- 1 ftp ftp 6211 /0/1980 0:0 FORMTE~1.RES
-rw-rw-rw- 1 ftp ftp 2864 /0/1980 0:0 RESOUR~1.CS
-rw-rw-rw- 1 ftp ftp 5612 /0/1980 0:0 RESOUR~1.RES
-rw-rw-rw- 1 ftp ftp 1089 /0/1980 0:0 SETTIN~1.CS
-rw-rw-rw- 1 ftp ftp 4286 /0/1980 0:0 swust.ico
Serv-U:
-rwxrw-r-- 1 user group 3014 Nov 12 14:57 cwinvnc337.ESn
-rwxrw-r-- 1 user group 20480 Mar 3 11:25 inmcsvr更新说明.ESn
-rwxrw-r-- 1 user group 450 Apr 13 11:39 对话框中加入工具条.ESn
Windows自带FTP:
-rwxrwxrwx 1 owner group 19041660 May 25 2004 VC.ESn
-rwxrwxrwx 1 owner group 450 Apr 6 15:04 对话框中加入工具条.ESn
再看UNIX格式,也拿出一条文件信息来讲:
-rwxrw-r-- 1 user group 3014 Nov 12 14:57 cwinvnc337.ESn
unix我不熟,每一段的意义不太清楚。但以上的格式分解为:第一段为-rwxrw-r–,第二段为1,第三段为user,第四段为group,第五段为文件长度,第六段为月,第七段为日,第八段为时间,第九段为文件名称。
需要注意的是:如果格式串的第一个字符为d,表示为一个目录信息,比如drwxrw-r–
另外,第八段有可能不是时间,而是年份,比如2005,从上面的例子中你可以发现。
很明显我这里格式错乱了,而且时间解析不对!
在FileZilla里可以显示出来了
可是!,文件名还是有问题,再度检查一下源代码!
引入time.h
https://www.jb51.net/article/238029.htm
strftime函数主要用于时间格式化,它的函数原型如下:
size_t __cdecl strftime(char * restrict _Buf,size_t _SizeInBytes,const char * restrict _Format,const struct tm * restrict _Tm);
- _Buf, 表示返回的时间字符串
- _SizeInBytes, 要写入的字节的最大数量
- _Format, 这是 C 字符串,包含了普通字符和特殊格式说明符的任何组合。
- _Tm, 输入时间结构体
时间结构体格式如下:
> struct tm {
int tm_sec; /* 秒,范围从 0 到 59 */
int tm_min; /* 分,范围从 0 到 59 */
int tm_hour; /* 小时,范围从 0 到 23 */
int tm_mday; /* 一月中的第几天,范围从 1 到 31 */
int tm_mon; /* 月份,范围从 0 到 11 */
int tm_year; /* 自 1900 起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
也就是说strftime函数的功能就是将时间结构体转换为指定的字符串格式。下面通过一个简单例子来演示strftime函数的用法。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char** argv)
{
time_t now_time;
struct tm *info;
char buffer[80];
time( &now_time );
info = localtime( &now_time );
strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", info);
printf("格式化的日期和时间 : %s \n", buffer );
return 0;
}
首先使用time函数获取当前时间,然后在使用strftime函数将时间通过指定的字符串格式打印出来,打印结果如下:
通过打印的结果可以看出,打印字符串的格式和函数中指定的字符串格式是一样的,这样通过对字符串格式的设置,就可按照自己的要求打印出时间和日期的字符串,使用起来更加灵活和方便。
2.PASV(被动模式)
站点发送PASV命令,尝试被动连接模式,FTP server返回IP地址与数据端口号;
站点连接FTP server返回的端口号(FTP server被动等待FTP client站点的连接)
命令: PASV
响应: 227 Entering Passive Mode (192,168,0,91,8,52)
代码实现:
int16_t cmd_PASV(char *outbuffer)
{
if (outbuffer) {
sprintf(outbuffer,("227 Entering Passive Mode (%i,%i,%i,%i,8,52)\r\n"),eeprom.myip[0][0],eeprom.myip[0][1],eeprom.myip[0][2],eeprom.myip[0][3]);
return strlen(outbuffer);
}
else {
return 0;
}
}
其中端口号的计算方式为:最后两位8、52 DATA PORT = 8*256+52
最新调试bug描述:
文件和目录可以正常浏览:也就是LIST CWD CDUP 指令通了
对于mkdir 好像没有设置到正确的返回指令;
删除文件指令是正常的.
删除文件夹指令:正常
存文件时:(STOR指令)
状态: 开始上传 E:\Test\Test.sln
状态: 文件传输成功,传输了 897 字节 (用时1 秒)
状态: 读取“/CYDZ”的目录列表...
命令: TYPE I
响应: 200 Using BINARY mode to transfer data.
命令: PASV
响应: 227 Entering Passive Mode (192,168,0,91,8,52)
命令: LIST
响应: 150 Here comes the directory listing.
响应: 226 Transfer complete. Closing data connection
错误: 传输连接被打断: ECONNABORTED - 连接中止
错误: 读取目录列表失败
状态: 读取“/CYDZ”的目录列表...
状态: 列出“/CYDZ”的目录成功
状态: 已从服务器断开
状态: 读取“/CYDZ”的目录列表...
状态: 列出“/CYDZ”的目录成功
状态: 读取“/CYDZ”的目录列表...
状态: 列出“/CYDZ”的目录成功
从这里看是文件传输完成从新发起了连接,并且完成PASV LIST的操作
但是这个时候又提示传输连接被打断:ECONNABORTED
-连接中止,这是怎么个情况呢?
-读取目录列表失败
对于RETR取文件指令的测试:
状态: 开始下载 /dir/swust.ico
命令: CWD /dir
响应: 250 Directory changed to /dir
命令: PWD
响应: 257 "/dir" is your current location
命令: TYPE A
响应: 200 Using ASCII mode to transfer data.
命令: PASV
响应: 227 Entering Passive Mode (192,168,0,91,8,52)
命令: RETR swust.ico
响应: 150 Opening ASCII mode data connection for (4286 bytes).
错误: 传输连接被打断: ECONNABORTED - 连接中止
不容乐观,传输没有成功
用调试助手看一看是怎么一回事:
确认当前目录正确,指令RETR swust.ico ,数据端口没有收到任何数据!