上一篇:
W5500以太网控制器芯片(四):实现DNS功能
使用w5500通过以太网口可以实现FTP功能。首先要了解FTP的基本命令和通讯方式。
FTP的本质是TCP连接,所以我们要实现一个TCP客户端,然后实现一些指定的FTP命令的发送和解析。
FTP基本命令:
命令 | 描述 |
ABOR | 中断数据连接程序 |
ACCT <account> | 系统特权帐号 |
ALLO <bytes> | 为服务器上的文件存储器分配字节 |
APPE <filename> | 添加文件到服务器同名文件 |
CDUP <dir path> | 改变服务器上的父目录 |
CWD <dir path> | 改变服务器上的工作目录 |
DELE <filename> | 删除服务器上的指定文件 |
HELP <command> | 返回指定命令信息 |
LIST <name> | 如果是文件名列出文件信息,如果是目录则列出文件列表 |
MODE <mode> | 传输模式(S=流模式,B=块模式,C=压缩模式) |
MKD <directory> | 在服务器上建立指定目录 |
NLST <directory> | 列出指定目录内容 |
NOOP | 无动作,除了来自服务器上的承认 |
PASS <password> | 系统登录密码 |
PASV | 请求服务器等待数据连接 |
PORT <address> | IP 地址和两字节的端口 ID |
PWD | 显示当前工作目录 |
QUIT | 从 FTP 服务器上退出登录 |
REIN | 重新初始化登录状态连接 |
REST <offset> | 由特定偏移量重启文件传递 |
RETR <filename> | 从服务器上找回(复制)文件 |
RMD <directory> | 在服务器上删除指定目录 |
RNFR <old path> | 对旧路径重命名 |
RNTO <new path> | 对新路径重命名 |
SITE <params> | 由服务器提供的站点特殊参数 |
SMNT <pathname> | 挂载指定文件结构 |
STAT <directory> | 在当前程序或目录上返回信息 |
STOR <filename> | 储存(复制)文件到服务器上 |
STOU <filename> | 储存文件到服务器名称上 |
STRU <type> | 数据结构(F=文件,R=记录,P=页面) |
SYST | 返回服务器使用的操作系统 |
TYPE <data type> | 数据类型(A=ASCII,E=EBCDIC,I=binary) |
USER <username> | 系统登录的用户名 |
FTP基本响应码:
响应代码 | 解释说明 |
110 | 新文件指示器上的重启标记 |
120 | 服务器准备就绪的时间(分钟数) |
125 | 打开数据连接,开始传输 |
150 | 打开连接 |
200 | 成功 |
202 | 命令没有执行 |
211 | 系统状态回复 |
212 | 目录状态回复 |
213 | 文件状态回复 |
214 | 帮助信息回复 |
215 | 系统类型回复 |
220 | 服务就绪 |
221 | 退出网络 |
225 | 打开数据连接 |
226 | 结束数据连接 |
227 | 进入被动模式(IP 地址、ID 端口) |
230 | 登录因特网 |
250 | 文件行为完成 |
257 | 路径名建立 |
331 | 要求密码 |
332 | 要求帐号 |
350 | 文件行为暂停 |
421 | 服务关闭 |
425 | 无法打开数据连接 |
426 | 结束连接 |
450 | 文件不可用 |
451 | 遇到本地错误 |
452 | 磁盘空间不足 |
500 | 无效命令 |
501 | 错误参数 |
502 | 命令没有执行 |
503 | 错误指令序列 |
504 | 无效命令参数 |
530 | 未登录网络 |
532 | 存储文件需要帐号 |
550 | 文件不可用 |
551 | 不知道的页类型 |
552 | 超过存储分配 |
553 | 文件名不允许 |
通过发送和解析这些命令和响应码,再加上TCP客户端和服务器,就可以分别实现FTP客户端和FTP服务器。
下面来实现一个FTP客户端,下载升级文件:
一般FTP分主动和被动模式,在主动模式下,通常21端口是命令端口,20端口是数据端口。在被动模式下,数据端口不一定。一般客户端可以先通知服务器,请求被动模式命令端口,然后再解析服务器的回复,得到被动端口,然后去连接,接收数据内容。
1、开始先定义一些参数:
//FTP服务socket通道
#define SOCK_FTP_CTRL 6
#define SOCK_FTP_DATA 7
un_l2cval remote_ip;
uint16_t remote_port;
uint16_t local_port;
uint8_t connect_state_control_ftpc = 0;
uint8_t connect_state_data_ftpc = 0;
uint8_t gModeActivePassiveflag = 0;
uint8_t gMenuStart = 0;
uint8_t gDataSockReady = 0;
uint8_t gDataPutGetStart = 0;
//FTP服务器地址
uint8_t FTP_destip[4] = {192, 1, 1, 1};
uint16_t FTP_destport = 0;
//FTP数据传输缓冲区
uint8_t FTP_data_buf[1500] = {0};
struct ftpc ftpc;
struct Command Command;
//以太网获取FTP状态
uint8_t ether_ftp_sta = 0;
//FTP文件大小
u32 ftp_file_size = 0;
//FTP文件句柄
int ftp_fd_w = 0;
void ftp_data_deal(u8 *buf, u16 len);
void ftpc_init(void);
uint8_t ftpc_run(void);
char proc_ftpc(char *buf);
int pportc(char *arg);
头文件定义:
#ifndef __W5500_FTP_H
#define __W5500_FTP_H
#include "bsp_include.h"
#include <stdint.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>
#include "w5500_socket.h"
#define LINELEN 128
#define _MAX_SS 1400
/* FTP Responses */
#define R_150 150 /* File status ok; opening data conn */
#define R_200 200 /* 'Generic' command ok */
#define R_220 220 /* Service ready for new user. */
#define R_226 226 /* Closing data connection. File transfer/abort successful */
#define R_227 227 /* Entering passive mode (h1,h2,h3,h4,p1,p2) */
#define R_230 230 /* User logged in, proceed */
#define R_331 331 /* User name okay, need password. */
#define TransferAscii 'A'
#define TransferBinary 'I'
enum ftpc_type {
ASCII_TYPE,
IMAGE_TYPE,
};
enum ftpc_datasock_state{
DATASOCK_IDLE,
DATASOCK_READY,
DATASOCK_START
};
enum ftpc_datasock_mode{
PASSIVE_MODE,
ACTIVE_MODE
};
enum CommandFirst {
f_nocmd,
f_dir,
f_put,
f_get,
};
enum CommandSecond {
s_nocmd,
s_dir,
s_put,
s_get,
};
struct Command {
enum CommandFirst First;
enum CommandSecond Second;
};
struct ftpc {
uint8_t control; /* Control stream */
uint8_t data; /* Data stream */
enum ftpc_type type; /* Transfer type */
enum ftpc_datasock_state dsock_state;
enum ftpc_datasock_mode dsock_mode;
char workingdir[LINELEN];
char filename[LINELEN];
};
typedef union _un_l2cval {
uint32_t lVal;
uint8_t cVal[4];
}un_l2cval;
void Ethernet_FTP_service_deal(void);
#endif
2、定义一个FTP服务,下载完成后退出
//初始化FTP Client
void ftpc_init(void)
{
connect_state_control_ftpc = 0;
connect_state_data_ftpc = 0;
gModeActivePassiveflag = 0;
gMenuStart = 0;
gDataSockReady = 0;
gDataPutGetStart = 0;
ftp_file_size = 0;
ftp_fd_w = 0;
//创建FTP命令socket
ftpc.dsock_mode = PASSIVE_MODE;
local_port = 35000;
w5500_socket(SOCK_FTP_CTRL, Sn_MR_TCP, FTP_destport, 0x0);
}
//以太网FTP服务处理
void Ethernet_FTP_service_deal(void)
{
//下载初始化
if (ether_ftp_sta == 0)
{
ether_ftp_sta = 1;
ftpc_init();
}
//下载完成
if (ether_ftp_sta > 2)
{
//关闭socket通道
w5500_disconnect(SOCK_FTP_DATA);
w5500_close(SOCK_FTP_DATA);
w5500_disconnect(SOCK_FTP_CTRL);
w5500_close(SOCK_FTP_CTRL);
if (ether_ftp_sta == 3) //下载成功
{
ol_print(DEBUG_CHN, 0, "ETH FTP success\n");
System_Sta.FTP_Sta = FTP_SUCCESS;
}
else if (ether_ftp_sta == 0xFF) //下载失败
{
ol_print(DEBUG_CHN, 0, "ETH FTP fail\n");
System_Sta.FTP_Sta = FTP_FAIL;
}
ether_ftp_sta = 0;
return;
}
ftpc_run(); //FTP客户端状态机
}
3、实现FTP下载文件的命令发送流程
(1)、连接FTP服务器,提供用户名密码。
(2)、通知FTP服务器被动模式,解析被动模式端口(PASV),打开数据通道
(3)、查询指定的文件在FTP服务器是否存在,获取文件大小(LIST),数据通道断开
(4)、再次请求数据通道端口(PASV),打开数据通道
(5)、分段读取文件(RETR),读取完成,数据通道断开
(6)、比较读取到的文件和之前查询的长度是否一致
//FTP客户端状态机
uint8_t ftpc_run(void)
{
uint16_t size = 0;
long ret = 0;
uint32_t recv_byte;
uint32_t remain_datasize;
uint8_t dat[50] = {
0,
};
//FTP命令通道处理
switch (getSn_SR(SOCK_FTP_CTRL))
{
case SOCK_ESTABLISHED:
{
//socket连接成功
if (!connect_state_control_ftpc)
{
ol_print(DEBUG_CHN, 0, "%d:FTP Connected\r\n", SOCK_FTP_CTRL);
strcpy(ftpc.workingdir, "/");
connect_state_control_ftpc = 1;
}
//有命令要执行
if (gMenuStart)
{
ol_print(DEBUG_CHN, 0, "FTP cmd start:%d\r\n", ether_ftp_sta);
gMenuStart = 0;
if (ether_ftp_sta == 1) //开始查询文件基本信息
{
if (ftpc.dsock_mode == PASSIVE_MODE) //被动模式下载
{
sprintf((char *)dat, "PASV\r\n");
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
Command.First = f_dir;
}
else
{
wiz_NetInfo gWIZNETINFO;
ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO);
sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%d\r\n", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff));
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
Command.First = f_dir;
gModeActivePassiveflag = 1;
}
}
else if (ether_ftp_sta == 2) //开始下载文件,需要重新分配一次端口
{
if (ftpc.dsock_mode == PASSIVE_MODE) //被动模式下载
{
sprintf((char *)dat, "PASV\r\n");
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
Command.First = f_get;
}
else
{
wiz_NetInfo gWIZNETINFO;
ctlnetwork(CN_GET_NETINFO, (void *)&gWIZNETINFO);
sprintf((char *)dat, "PORT %d,%d,%d,%d,%d,%d\r\n", gWIZNETINFO.ip[0], gWIZNETINFO.ip[1], gWIZNETINFO.ip[2], gWIZNETINFO.ip[3], (uint8_t)(local_port >> 8), (uint8_t)(local_port & 0x00ff));
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
Command.First = f_get;
gModeActivePassiveflag = 1;
}
}
}
//数据通道就绪,可以开始发送FTP命令
if (gDataSockReady)
{
gDataSockReady = 0;
switch (Command.First)
{
case f_dir: //列出要下载的FTP文件信息
// sprintf(dat, "MLSD\r\n");
sprintf((char *)dat, "LIST %s\r\n", System_Para.Filename);
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
break;
case f_get:
ol_print(DEBUG_CHN, 0, ">get file name?%s\n", System_Para.Filename);
sprintf((char *)dat, "RETR %s\r\n", System_Para.Filename);
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
break;
}
}
//接收到FTP命令通道数据
if ((size = getSn_RX_RSR(SOCK_FTP_CTRL)) > 0)
{
// Don't need to check SOCKERR_BUSY because it doesn't not occur.
memset(FTP_data_buf, 0, _MAX_SS);
if (size > _MAX_SS)
size = _MAX_SS - 1;
ret = w5500_recv(SOCK_FTP_CTRL, FTP_data_buf, size);
FTP_data_buf[ret] = '\0';
if (ret != size)
{
if (ret == SOCK_BUSY)
return 0;
if (ret < 0)
{
ol_print(DEBUG_CHN, 0, "%d:recv() error:%ld\r\n", SOCK_FTP_CTRL, ret);
w5500_close(SOCK_FTP_CTRL);
//命令通道接收异常,关闭FTP下载
ether_ftp_sta = 0xFF;
return ret;
}
}
ol_print(DEBUG_CHN, 0, "Rcvd Command: %s\r\n", FTP_data_buf);
proc_ftpc((char *)FTP_data_buf); //处理命令
}
break;
}
case SOCK_CLOSE_WAIT: //SOCKET关闭等待
{
ol_print(DEBUG_CHN, 0, "%d:CloseWait\r\n", SOCK_FTP_CTRL);
if ((ret = w5500_disconnect(SOCK_FTP_CTRL)) != SOCK_OK)
return ret;
ol_print(DEBUG_CHN, 0, "%d:Closed\r\n", SOCK_FTP_CTRL);
break;
}
case SOCK_CLOSED: //SOCKET关闭状态
{
ol_print(DEBUG_CHN, 0, "%d:FTPStart\r\n", SOCK_FTP_CTRL);
if ((ret = w5500_socket(SOCK_FTP_CTRL, Sn_MR_TCP, FTP_destport, 0x0)) != SOCK_FTP_CTRL)
{
ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_CTRL, ret);
w5500_close(SOCK_FTP_CTRL);
return ret;
}
break;
}
case SOCK_INIT: //SOCKET初始化状态
{
ol_print(DEBUG_CHN, 0, "%d:Opened\r\n", SOCK_FTP_CTRL);
if ((ret = w5500_connect(SOCK_FTP_CTRL, FTP_destip, FTP_destport)) != SOCK_OK)
{
ol_print(DEBUG_CHN, 0, "%d:Connect error\r\n", SOCK_FTP_CTRL);
return ret;
}
connect_state_control_ftpc = 0;
ol_print(DEBUG_CHN, 0, "%d:Connectting...\r\n", SOCK_FTP_CTRL);
break;
}
}
//FTP数据通道
switch (getSn_SR(SOCK_FTP_DATA))
{
case SOCK_ESTABLISHED:
{
if (!connect_state_data_ftpc)
{
ol_print(DEBUG_CHN, 0, "%d:FTP Data socket Connected\r\n", SOCK_FTP_DATA);
connect_state_data_ftpc = 1;
}
//开始接收数据
if (gDataPutGetStart)
{
ol_print(DEBUG_CHN, 0, "%d:rec ftp data:%d\r\n", SOCK_FTP_DATA, Command.Second);
switch (Command.Second)
{
case s_dir: //查询文件信息是小文本,发送完后数据通道会直接断开,此段程序一般无法进入
{
while (1)
{
if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0)
{
while (1)
{
memset(FTP_data_buf, 0, _MAX_SS);
if (remain_datasize > _MAX_SS)
recv_byte = _MAX_SS;
else
recv_byte = remain_datasize;
ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);
if (ret > 0)
{
ftp_data_deal(FTP_data_buf, ret);
}
else
{
//传输失败,结束FTP下载
ether_ftp_sta = 0xFF;
return 0;
}
remain_datasize -= ret;
if (remain_datasize <= 0)
break;
}
}
else
{
if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)
break;
}
}
gDataPutGetStart = 0;
Command.Second = s_nocmd;
break;
}
case s_get:
{
while (1)
{
if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0)
{
while (1)
{
memset(FTP_data_buf, 0, _MAX_SS);
if (remain_datasize > _MAX_SS)
recv_byte = _MAX_SS;
else
recv_byte = remain_datasize;
ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);
if (ret > 0)
{
ftp_data_deal(FTP_data_buf, ret);
}
else
{
//传输失败,结束FTP下载
ether_ftp_sta = 0xFF;
return 0;
}
remain_datasize -= ret;
if (remain_datasize <= 0)
break;
}
}
else
{
if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)
break;
}
}
gDataPutGetStart = 0;
Command.Second = s_nocmd;
break;
}
}
}
break;
}
case SOCK_CLOSE_WAIT:
{
//对于较小的数据,一次传输后FTP数据通道会直接关闭,所以在SOCK_CLOSE_WAIT状态下也要先接收一次数据
if (gDataPutGetStart)
{
ol_print(DEBUG_CHN, 0, "%d:rec ftp data wait:%d\r\n", SOCK_FTP_DATA, Command.Second);
switch (Command.Second)
{
case s_dir:
{
while (1)
{
if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0)
{
while (1)
{
memset(FTP_data_buf, 0, _MAX_SS);
if (remain_datasize > _MAX_SS)
recv_byte = _MAX_SS;
else
recv_byte = remain_datasize;
ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);
if (ret > 0)
{
ftp_data_deal(FTP_data_buf, ret);
}
else
{
//传输失败,结束FTP下载
ether_ftp_sta = 0xFF;
return 0;
}
remain_datasize -= ret;
if (remain_datasize <= 0)
break;
}
}
else
{
if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)
break;
}
}
gDataPutGetStart = 0;
Command.Second = s_nocmd;
break;
}
case s_get:
{
while (1)
{
if ((remain_datasize = getSn_RX_RSR(SOCK_FTP_DATA)) > 0)
{
while (1)
{
memset(FTP_data_buf, 0, _MAX_SS);
if (remain_datasize > _MAX_SS)
recv_byte = _MAX_SS;
else
recv_byte = remain_datasize;
ret = w5500_recv(SOCK_FTP_DATA, FTP_data_buf, recv_byte);
if (ret > 0)
{
ftp_data_deal(FTP_data_buf, ret);
}
else
{
//传输失败,结束FTP下载
ether_ftp_sta = 0xFF;
return 0;
}
remain_datasize -= ret;
if (remain_datasize <= 0)
break;
}
}
else
{
if (getSn_SR(SOCK_FTP_DATA) != SOCK_ESTABLISHED)
break;
}
}
gDataPutGetStart = 0;
Command.Second = s_nocmd;
break;
}
}
}
ol_print(DEBUG_CHN, 0, "%d:CloseWait\r\n", SOCK_FTP_DATA);
if ((ret = w5500_disconnect(SOCK_FTP_DATA)) != SOCK_OK)
{
//无法正常关闭数据通道,FTP失败
ether_ftp_sta = 0xFF;
return 0;
}
else
{
//关闭数据通道成功,数据解析完毕,进入FTP下一步
if (ether_ftp_sta == 1) //文件信息读取结束
{
ether_ftp_sta = 2;
gMenuStart = 1;
}
else if (ether_ftp_sta == 2) //文件下载结束,准备升级
{
ether_ftp_sta = 3;
vfs_close(ftp_fd_w);
//读取文件大小
ret = vfs_file_size("/fota/ftptext.pack");
ol_print(DEBUG_CHN, 0, "FTP file size:%d,%d\n", ftp_file_size, ret);
if (ftp_file_size != System_Para.Filesize)
{
ether_ftp_sta = 0xFF;
}
ret = vfs_rename("/fota/ftptext.pack", "/fota/fota.pack");
if (ret != 0)
{
ether_ftp_sta = 0xFF;
}
}
}
ol_print(DEBUG_CHN, 0, "%d:Closed\r\n", SOCK_FTP_DATA);
break;
}
case SOCK_CLOSED:
{
if (ftpc.dsock_state == DATASOCK_READY)
{
if (ftpc.dsock_mode == PASSIVE_MODE)
{
ol_print(DEBUG_CHN, 0, "%d:FTPDataStart, port : %d\r\n", SOCK_FTP_DATA, local_port);
if ((ret = w5500_socket(SOCK_FTP_DATA, Sn_MR_TCP, local_port, 0x0)) != SOCK_FTP_DATA)
{
ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_DATA, ret);
w5500_close(SOCK_FTP_DATA);
return ret;
}
local_port++;
if (local_port > 50000)
local_port = 35000;
}
else
{
ol_print(DEBUG_CHN, 0, "%d:FTPDataStart, port : %d\r\n", SOCK_FTP_DATA, local_port);
if ((ret = w5500_socket(SOCK_FTP_DATA, Sn_MR_TCP, local_port, 0x0)) != SOCK_FTP_DATA)
{
ol_print(DEBUG_CHN, 0, "%d:socket() error:%ld\r\n", SOCK_FTP_DATA, ret);
w5500_close(SOCK_FTP_DATA);
return ret;
}
local_port++;
if (local_port > 50000)
local_port = 35000;
}
ftpc.dsock_state = DATASOCK_START;
}
break;
}
case SOCK_INIT:
{
ol_print(DEBUG_CHN, 0, "%d:Opened\r\n", SOCK_FTP_DATA);
if (ftpc.dsock_mode == ACTIVE_MODE)
{
if ((ret = w5500_listen(SOCK_FTP_DATA)) != SOCK_OK)
{
OSI_LOGI(0, "w5500 FTP Listen error %d", SOCK_FTP_DATA);
return ret;
}
gDataSockReady = 1;
OSI_LOGI(0, "w5500 FTP Listen ok:%d", SOCK_FTP_DATA);
}
else
{
if ((ret = w5500_connect(SOCK_FTP_DATA, remote_ip.cVal, remote_port)) != SOCK_OK)
{
ol_print(DEBUG_CHN, 0, "%d:Connect error\r\n", SOCK_FTP_DATA);
return ret;
}
gDataSockReady = 1; //FTP数据通道开启完成,准备执行命令
}
connect_state_data_ftpc = 0;
break;
}
}
return 0;
}
解析FTP相应码:
//FTP服务器回复的协议解析
char proc_ftpc(char *buf)
{
uint16_t Responses;
uint8_t dat[30] = {
0,
};
Responses = (buf[0] - '0') * 100 + (buf[1] - '0') * 10 + (buf[2] - '0');
switch (Responses)
{
case R_220: /* Service ready for new user. */
ol_print(DEBUG_CHN, 0, "USER %s\n", System_Para.User);
sprintf((char *)dat, "USER %s\r\n", System_Para.User);
ol_print(DEBUG_CHN, 0, "\r\n");
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
break;
case R_331: /* User name okay, need password. */
ol_print(DEBUG_CHN, 0, "PASS %s\n", System_Para.Pass);
sprintf((char *)dat, "PASS %s\r\n", System_Para.Pass);
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
break;
case R_230: //登录成功,通知FTP服务器使用ASCII通讯
ol_print(DEBUG_CHN, 0, "\r\nlogged in, proceed\r\n");
sprintf((char *)dat, "TYPE %c\r\n", TransferAscii);
ftpc.type = ASCII_TYPE;
w5500_send(SOCK_FTP_CTRL, (uint8_t *)dat, strlen((char *)dat));
break;
case R_200: //设置ASCII模式完成,服务器回复成功
{
if ((ftpc.dsock_mode == ACTIVE_MODE) && gModeActivePassiveflag)
{
ftpc.dsock_state = DATASOCK_READY;
gModeActivePassiveflag = 0;
}
else
{
gMenuStart = 1;
}
break;
}
case R_150: //命令发送成功,开始传输数据
{
switch (Command.First)
{
case f_dir:
Command.First = f_nocmd;
Command.Second = s_dir;
gDataPutGetStart = 1;
break;
case f_get:
Command.First = f_nocmd;
Command.Second = s_get;
gDataPutGetStart = 1;
break;
}
break;
}
case R_226: //数据通道结束连接
gMenuStart = 1;
break;
case R_227: //被动模式设置成功,服务器返回被动模式数据端口
{
if (pportc(buf) == -1)
{
ol_print(DEBUG_CHN, 0, "Bad port syntax\r\n");
//转换被动模式失败,结束FTP
ether_ftp_sta = 0xFF;
}
else
{
ol_print(DEBUG_CHN, 0, "Go Open Data Sock...\r\n ");
ftpc.dsock_mode = PASSIVE_MODE;
ftpc.dsock_state = DATASOCK_READY; //开启FTP数据通道
}
break;
}
default:
OSI_LOGI(0, "w5500 FTP Default Status:%d", (uint16_t)Responses);
gDataSockReady = 1;
break;
}
return 1;
}
计算被动模式端口:
//计算被动模式连接端口
int pportc(char *arg)
{
int i;
char *tok = 0;
strtok(arg, "(");
for (i = 0; i < 4; i++)
{
if (i == 0)
tok = strtok(NULL, ",\r\n");
else
tok = strtok(NULL, ",");
remote_ip.cVal[i] = (uint8_t)atoi(tok);
if (!tok)
{
ol_print(DEBUG_CHN, 0, "bad pport : %s\r\n", arg);
return -1;
}
}
remote_port = 0;
for (i = 0; i < 2; i++)
{
tok = strtok(NULL, ",\r\n");
remote_port <<= 8;
remote_port += atoi(tok);
if (!tok)
{
ol_print(DEBUG_CHN, 0, "bad pport : %s\r\n", arg);
return -1;
}
}
ol_print(DEBUG_CHN, 0, "ip : %d.%d.%d.%d, port : %d\r\n", remote_ip.cVal[0], remote_ip.cVal[1], remote_ip.cVal[2], remote_ip.cVal[3], remote_port);
return 0;
}
5、处理FTP接收数据
仅供参考,文件参数信息解析的是filezilla服务器的默认信息。文件原始数据的接收也要根据w5500设备所在的文件存储类型(文件系统或者是无系统的FLASH)自行处理。
//查找下一空格后的第一个字符
s32 search_space(char *buf, u16 max_len)
{
char *p = buf;
u8 falg = 0, res = 0;
while (falg < 2)
{
if (*p == 0x20)
{
falg = 1;
}
else
{
if (falg == 1)
{
falg = 2;
break;
}
}
res++;
if (res > max_len)
{
return 0xff;
}
p++;
}
return res;
}
//接收处理FTP文件、命令协议
void ftp_data_deal(u8 *buf, u16 len)
{
u8 pos = 0;
u32 file_size = 0;
s32 ret;
//-r--r--r-- 1 ftp ftp 89344 May 20 2021 FTP.bin
//查询文件信息解析文件大小
if (Command.Second == s_dir)
{
ol_print(DEBUG_CHN, 0, "FTP file info,%d:%s", len, buf);
//解析过第一个空格 文件权限
pos = search_space((char *)buf, len);
if (pos == 0xFF)
return;
buf += pos;
//解析第二个空格 文件硬连接数
len = len - pos;
pos = search_space((char *)buf, len);
if (pos == 0xFF)
return;
buf += pos;
//解析第三个空格 文件所有人
len = len - pos;
pos = search_space((char *)buf, len);
if (pos == 0xFF)
return;
buf += pos;
//解析第四个空格 文件所有组
len = len - pos;
pos = search_space((char *)buf, len);
if (pos == 0xFF)
return;
buf += pos;
//解析文件大小
file_size = Str2Num((char *)buf, &pos);
ol_print(DEBUG_CHN, 0, "FTP file size:%d\r\n", file_size);
System_Para.Filesize = file_size;
}
else if (Command.Second == s_get) //下载文件
{
OSI_LOGI(0, "w5500 FTP page size:%d", len);
//创建文件
if (ftp_file_size == 0)
{
//打开文件
vfs_mkdir("/fota", 0);
ftp_fd_w = vfs_open("/fota/ftptext.pack", O_CREAT | O_RDWR | O_TRUNC);
if (ftp_fd_w < 0)
{
ol_print(DEBUG_CHN, 0, "FTP file open fail\r\n");
vfs_close(ftp_fd_w);
ether_ftp_sta = 0xFF;
return;
}
}
//传输文件
ret = vfs_write(ftp_fd_w, buf, len);
if (ret <= 0)
{
ol_print(DEBUG_CHN, 0, "FTP file write fail,%d\r\n", ftp_file_size);
vfs_close(ftp_fd_w);
ether_ftp_sta = 0xFF;
return;
}
else if (ret < len) //一次未传输完成
{
pos = len - ret;
ret = vfs_write(ftp_fd_w, buf, pos);
if (ret != pos)
{
ol_print(DEBUG_CHN, 0, "FTP file write fail,%d\r\n", ftp_file_size);
vfs_close(ftp_fd_w);
ether_ftp_sta = 0xFF;
return;
}
}
else
{
ftp_file_size += len;
}
OSI_LOGI(0, "w5500 FTP total size:%d", ftp_file_size);
}
}