W5500以太网控制器芯片(五):实现FTP客户端


#W5500以太网控制芯片相关文章链接#

上一篇:

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);
    }
}

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
W5500是一款嵌入式以控制器芯片,可以用于实现FTP客户功能。根据引用\[1\]中的描述,虽然本文主要讨论了TFTP协议的设计方案,但是类似的分析方法也可以用于实现稍微复杂一点的FTP协议。FTP协议是一种用于文件传输的协议,可以通过W5500芯片实现FTP客户功能。 根据引用\[2\]中的描述,TFTP基于UDP协议实现,而W5500芯片支持UDP协议。因此,可以使用W5500芯片的UDP功能来实现FTP客户的数据传输。 具体实现FTP客户功能的步骤如下: 1. 初始化W5500芯片,包括配置络参数和初始化UDP功能。 2. 建立与FTP服务器的连接,可以使用W5500芯片的TCP功能来建立连接。 3. 发送FTP命令,例如登录、切换目录、下载文件等。可以使用W5500芯片的UDP功能发送FTP命令。 4. 接收FTP服务器的响应,可以使用W5500芯片的UDP功能接收FTP服务器的响应。 5. 根据FTP服务器的响应进行相应的处理,例如解析文件列表、下载文件等。 6. 断开与FTP服务器的连接,释放资源。 需要注意的是,FTP协议相对复杂,实现FTP客户功能可能需要更多的代码和处理逻辑。具体的实现细节可以参考相关的FTP协议规范和W5500芯片的开发文档。 总之,通过使用W5500芯片的UDP功能,可以实现FTP客户功能。具体的实现步骤和细节可以根据FTP协议规范和W5500芯片的开发文档进行参考和实现。 #### 引用[.reference_title] - *1* *2* *3* [基于W5500的嵌入式TFTP服务器实现](https://blog.csdn.net/WIZnet2012/article/details/48157319)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值