第十九章 W55MH32 FTP_Client示例

目录

1 FTP协议简介

2 FTP协议特点

3 FTP协议应用场景

4 FTP工作流程

5 主动模式与被动模式详解

6 FTP客户端实现功能

7 FTP报文解析

8 实现过程

9 运行结果

10 总结


本篇文章,我们将详细介绍如何在W55MH32芯片上面实现FTP协议的客户端模式。并通过实战例程,为大家讲解如何在W55MH32上使用FTP协议的客户端模式来访问FTP服务器并下载文件。

该例程用到的其他网络协议,例如DHCP,请参考相关章节。有关 W55MH32 的初始化过程,请参考 Network Install 章节,这里将不再赘述。

1 FTP协议简介

FTP(File Transfer Protocol,文件传输协议)是一种标准的网络协议,用于在客户端和服务器之间传输文件。FTP 客户端协议是基于 FTP 协议实现的,用来指导客户端如何与 FTP 服务器通信,实现文件的上传、下载、目录操作等功能。由于 FTP 最初是以明文传输设计的,不够安全,在 FTP 上加入 SSL/TLS 加密层,可提供加密的控制连接和数据连接。以下是 FTP 客户端协议的主要内容和工作机制的介绍。

2 FTP协议特点

  1. 基于 TCP 传输:
  1. FTP 使用两个 TCP 连接:控制连接(端口 21)和数据连接(端口 20 或 PASV 模式动态分配的端口),确保可靠的数据传输。
  1. 分离控制与数据:
  1. 控制连接用于发送命令和接收响应。
  2. 数据连接用于文件内容或目录信息的传输。
  1. 支持多种传输模式:
  1. 主动模式(Active Mode):服务器主动连接客户端的数据端口。
  2. 被动模式(Passive Mode):客户端主动连接服务器提供的数据端口,解决 NAT 防火墙限制。
  1. 支持多种文件操作:
  1. 文件上传(STOR)、下载(RETR)、删除(DELE)。
  2. 目录操作(MKD、RMD、CWD、PWD)。
  3. 获取文件列表(LIST、NLST)。
  1. 明文传输(传统 FTP):
  1. 用户名、密码及数据以明文形式传输,不安全。
  2. 安全改进:FTPS(FTP Secure,基于 SSL/TLS)和 SFTP(Secure File Transfer Protocol,基于 SSH)。
  1. 灵活的用户认证机制:
  1. 支持匿名登录(匿名用户可通过 email 作为密码)。
  2. 支持认证用户名和密码。

3 FTP协议应用场景

接下来,我们了解下在W55MH32上,可以使用FTP协议完成哪些操作及应用呢?

版本升级: 嵌入式设备通过 FTP 客户端从远程服务器下载最新的固件文件并执行升级。

日志下载:嵌入式设备在运行中生成日志、监测数据或用户交互记录,通过 FTP 客户端上传到远程服务器。

配置保存:用户可以将配置文件上传到FTP服务器进行备份或共享。

离线模式数据同步:嵌入式设备在脱机运行时本地保存数据,在网络恢复后通过 FTP 同步到服务器。

4 FTP工作流程

1. 建立控制连接

  1. 客户端初始化:客户端启动 FTP 客户端程序,指定要连接的 FTP 服务器的地址和端口号(端口号为 21)。
  2. TCP 连接建立:客户端通过 TCP 协议向服务器的 21 端口发起连接请求。服务器监听该端口,接收到请求后,与客户端建立起一条 TCP 连接,这个连接被称为控制连接,主要用于传输 FTP 命令和服务器的响应信息。
  3. 身份验证:连接建立后,服务器会提示客户端输入用户名和密码进行身份验证。客户端发送相应的用户名和密码信息到服务器,服务器验证通过后,才允许客户端进行后续操作。也有一些匿名 FTP 服务器,允许用户以 “anonymous” 作为用户名,以电子邮件地址作为密码进行登录,提供公开的文件访问服务 。

2. 传输模式选择

客户端和服务器在控制连接上协商数据传输模式,主要有两种模式:

  1. 主动模式(PORT 模式):客户端通过控制连接告诉服务器自己的数据端口(客户端随机开放的一个端口),服务器使用 20 端口主动连接客户端的数据端口来传输数据。
  2. 被动模式(PASV 模式):客户端发送 PASV 命令给服务器,服务器在控制连接上告知客户端自己开放的一个临时数据端口(通常是 1024 以上的端口),然后客户端使用自己的一个随机端口连接服务器的这个临时数据端口来传输数据。

3. 数据传输

根据用户的操作需求,通过数据连接进行文件或目录相关操作:

  1. 上传文件:客户端向服务器发送 STOR(存储)命令,然后通过数据连接将本地文件数据发送到服务器。服务器接收到数据后,将其存储在指定的目录下。
  2. 下载文件:客户端向服务器发送 RETR(检索)命令,请求下载服务器上的文件。服务器通过数据连接将文件数据发送给客户端,客户端接收数据并将其保存到本地指定位置。
  3. 目录操作:客户端还可以发送诸如 LIST(列出目录内容)、CWD(更改工作目录)、MKD(创建目录)、RMD(删除目录)等命令,服务器执行相应操作,并通过控制连接返回操作结果。执行这些命令时,若需要传输目录列表等数据,也会通过数据连接进行传输。

4. 关闭连接

  1. 数据连接关闭:在完成文件传输或其他操作后,数据连接会被关闭。如果还有其他操作需要进行,客户端和服务器可以根据需要重新建立数据连接。
  2. 控制连接关闭:当客户端完成所有操作后,会向服务器发送 QUIT 命令,服务器接收到该命令后,会关闭控制连接。至此,客户端与服务器之间的 FTP 会话结束。

5 主动模式与被动模式详解

主动模式(Active Mode):

  1. 客户端打开一个端口并监听。
  2. 客户端通过控制连接告诉服务器自己的 IP 和端口。
  3. 服务器主动连接到客户端指定的端口传输数据。

被动模式(Passive Mode):

  1. 客户端通过控制连接请求被动模式。
  2. 服务器打开一个随机端口并通过控制连接告知客户端。
  3. 客户端主动连接到服务器指定的端口传输数据。

优缺点对比:

  1. 主动模式更适合在服务器端网络无防火墙限制的环境。
  2. 被动模式更适合客户端在 NAT 或防火墙后的情况。

6 FTP客户端实现功能

FTP 客户端的核心功能包括:

  1. 连接到 FTP 服务器。
  2. 用户身份认证。
  3. 上传和下载文件。
  4. 浏览和管理服务器目录结构。
  5. 支持主动模式和被动模式的切换。
  6. 提供断点续传功能(某些高级客户端)。

7 FTP报文解析

FTP 报文分为命令和响应报文,命令报文用于发送操作请求,响应报文用于返回结果。

命令报文格式为“<命令> <参数>\r\n”,字段解释如下:

  1. <命令>:FTP命令(如 USER、PASS)。
  2. <参数>:命令的附加信息(如用户名、文件名)。

例如“USER username\r\n”。常见的命令包括登录 (USER, PASS)、文件操作 (RETR, STOR)、目录操作 (LIST, CWD) 等。每个 FTP 报文由命令或响应代码、状态码及附加数据组成,状态码用于指示操作结果。

以下是 FTP 常见命令:

  1. USER: 提供用户名进行身份验证。
  2. PASS: 提供密码进行身份验证。
  3. CWD: 更改当前工作目录。
  4. PWD: 显示当前工作目录。
  5. LIST: 列出目录下的文件和子目录。
  6. RETR: 从服务器下载文件。
  7. STOR: 上传文件到服务器。
  8. DELE: 删除指定文件。
  9. MKD: 创建新目录。
  10. RMD: 删除目录。
  11. QUIT: 终止会话并退出。
  12. TYPE: 设置文件传输类型(ASCII 或 Binary)。
  13. PORT: 指定数据连接的端口。
  14. PASV: 启用被动模式,服务器指定端口供客户端连接。

响应报文格式为“<状态码> <说明文字>\r\n”,字段解释如下:

  1. <状态码>:三位数字表示状态。
  2. <说明文字>:状态的文字描述。

例如“230 User logged in, proceed.\r\n”。以下是FTP常见的响应码:

  1. 1xx(信息性响应): 主要是提供一些初步的信息,通常表示服务器正在处理请求,还没有完成操作。
  2. 2xx(成功响应): 表示命令成功执行。这是客户端最希望看到的响应类型之一,说明请求的操作(如登录、文件传输等)顺利完成。
  3. 3xx(补充信息响应): 表示服务器需要一些额外的信息才能完成操作。通常是在身份验证或者文件定位等过程中出现。
  4. 4xx(暂时错误响应): 表示客户端的请求有问题,但错误是暂时的,可能通过一些调整(如重新发送请求等)可以解决。
  5. 5xx(永久性错误响应): 表示客户端的请求存在错误,并且这个错误是比较严重的,很难通过简单的调整来纠正。

接着我们来看看FTP获取目录的报文示例:

  1. 客户端建立TCP连接到服务器的21端口
  2. 服务器返回:220 Welcome to FTP Server\r\n
  3. 客户端发送:USER wiznet\r\n
  4. 服务器返回:331 User wiznet OK.Password required\r\n
  5. 客户端发送:PASS wiznet\r\n
  6. 服务器返回:230 User logged in\r\n
  7. 客户端发送PORT 192,168,1,5,20,100\r\n(主动模式,192,168,1,5是客户端的地址,20,100是客户端期望的端口号20*256+100=5260)
  8. 服务器返回:200 PORT command successful\r\n
  9. 客户端发送:LIST\r\n(DIR命令,获取当前目录的文件信息)
  10. 服务器回复:150 Opening ASCII mode data connection for file list\r\n
  11. 服务器像客户端期望的端口号发起TCP连接,并传输目录信息,传输完成后关闭TCP连接。
  12. 客户端发送:QUIT\r\n(退出FTP会话)
  13. 服务器回复:221 Goodbye\r\n

8 实现过程

接下来,我们看看如何在W55MH32上实现FTP协议Client模式。

注意:测试实例需要PC端和W55MH32处于同一网段。

步骤一:FTP Client模式初始化

void ftpc_init(uint8_t *src_ip, uint8_t sn_ctrl, uint8_t sn_data)
{
    ftpc.dsock_mode = ACTIVE_MODE;

    local_ip.cVal[0] = src_ip[0];
    local_ip.cVal[1] = src_ip[1];
    local_ip.cVal[2] = src_ip[2];
    local_ip.cVal[3] = src_ip[3];
    local_port       = 35000;
    socket_ctrl      = sn_ctrl;
    socket_data      = sn_data;
    strcpy(ftpc.workingdir, "/");
    socket(socket_ctrl, Sn_MR_TCP, FTP_destport, 0x0);
}

ftpc_init()函数的主要作用是初始化FTP客户端的配置和状态,包括设置传输模式、本地IP地址和端口、socket、工作目录,并创建用于命令传输的控制socket。

步骤二:在主循环中运行ftpc_run()函数

    while (1)
    {
        ftpc_run(ethernet_buf);
    }

ftpc_run()函数如下所示:

uint8_t ftpc_run(uint8_t *dbuf)
{
    uint16_t size = 0;
    long     ret  = 0;
#if defined(F_FILESYSTEM)
    uint32_t send_byte;
#endif
    uint32_t recv_byte;
    uint32_t blocklen;
    uint32_t remain_filesize;
    uint32_t remain_datasize;
    uint8_t  dat[50] = {
        0,
    };

    switch (getSn_SR(socket_ctrl))
    {
    case SOCK_ESTABLISHED:
        if (!connect_state_control_ftpc)
        {
            printf("%d:FTP Connected\r\n", socket_ctrl);
            strcpy(ftpc.workingdir, "/");
            connect_state_control_ftpc = 1;
        }
        if (gMenuStart)
        {
            gMenuStart = 0;
            printf("\r\n----------------------------------------\r\n");
            printf("Press menu key\r\n");
            printf("----------------------------------------\r\n");
            printf("1> View FTP Server Directory\r\n");
            printf("2> View My Directory\r\n");
            printf("3> Sets the type of file to be transferred. Current state : %s\r\n", (ftpc.type == ASCII_TYPE) ? "Ascii" : "Binary");
            printf("4> Sets Data Connection. Current state : %s\r\n", (ftpc.dsock_mode == ACTIVE_MODE) ? "Active" : "Passive");
            printf("5> Put File to Server\r\n");
            printf("6> Get File from Server\r\n");
#if defined(F_FILESYSTEM)
            printf("7> Delete My File\r\n");
#endif
            printf("----------------------------------------\r\n");
            while (1)
            {
                memset(gMsgBuf, 0, sizeof(gMsgBuf));
                scanf("%s", gMsgBuf);
                if (gMsgBuf[0] == '1')
                {
                    if (ftpc.dsock_mode == PASSIVE_MODE)
                    {
                        sprintf((char *)dat, "PASV\r\n");
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_dir;
                        break;
                    }
                    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));
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_dir;

                        gModeActivePassiveflag = 1;
                        break;
                    }
                }
                else if (gMsgBuf[0] == '5')
                {
                    if (ftpc.dsock_mode == PASSIVE_MODE)
                    {
                        sprintf((char *)dat, "PASV\r\n");
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_put;
                        break;
                    }
                    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));
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_put;

                        gModeActivePassiveflag = 1;
                        break;
                    }
                }
                else if (gMsgBuf[0] == '6')
                {
                    if (ftpc.dsock_mode == PASSIVE_MODE)
                    {
                        sprintf((char *)dat, "PASV\r\n");
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_get;
                        break;
                    }
                    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));
                        send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                        Command.First = f_get;

                        gModeActivePassiveflag = 1;
                        break;
                    }
                }
                else if (gMsgBuf[0] == '2')
                {
#if defined(F_FILESYSTEM)
                    scan_files(ftpc.workingdir, dbuf, (int *)&size);
                    printf("\r\n%s\r\n", dbuf);
#else
                    if (strncmp(ftpc.workingdir, "/$Recycle.Bin", sizeof("/$Recycle.Bin")) != 0)
                        size = sprintf((char *)dbuf, "drwxr-xr-x 1 ftp ftp 0 Dec 31 2014 $Recycle.Bin\r\n-rwxr-xr-x 1 ftp ftp 512 Dec 31 2014 test.txt\r\n");
                    printf("\r\n%s\r\n", dbuf);
#endif
                    gMenuStart = 1;
                    break;
                }
                else if (gMsgBuf[0] == '3')
                {
                    printf("1> ASCII\r\n");
                    printf("2> BINARY\r\n");
                    while (1)
                    {
                        memset(gMsgBuf, 0, sizeof(gMsgBuf));
                        scanf("%s", gMsgBuf);
                        if (gMsgBuf[0] == '1')
                        {
                            sprintf((char *)dat, "TYPE %c\r\n", TransferAscii);
                            ftpc.type = ASCII_TYPE;
                            send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                            break;
                        }
                        else if (gMsgBuf[0] == '2')
                        {
                            sprintf((char *)dat, "TYPE %c\r\n", TransferBinary);
                            ftpc.type = IMAGE_TYPE;
                            send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                            break;
                        }
                        else if (gMsgBuf[0] != 0x00)
                        {
                            printf("\r\nRetry...\r\n");
                        }
                    }
                    break;
                }
                else if (gMsgBuf[0] == '4')
                {
                    printf("1> ACTIVE\r\n");
                    printf("2> PASSIVE\r\n");
                    while (1)
                    {
                        memset(gMsgBuf, 0, sizeof(gMsgBuf));
                        scanf("%s", gMsgBuf);
                        if (gMsgBuf[0] == '1')
                        {
                            ftpc.dsock_mode = ACTIVE_MODE;
                            break;
                        }
                        else if (gMsgBuf[0] == '2')
                        {
                            ftpc.dsock_mode = PASSIVE_MODE;
                            break;
                        }
                        else if (gMsgBuf[0] != 0x00)
                        {
                            printf("\r\nRetry...\r\n");
                        }
                    }
                    gMenuStart = 1;
                    break;
                }
#if defined(F_FILESYSTEM)
                else if (msg_c == '7')
                {
                    printf(">del filename?");
                    memset(gMsgBuf, 0, sizeof(gMsgBuf));
                    scanf("%s", gMsgBuf);
                    sprintf((char *)dat, "STOR %s\r\n", gMsgBuf);
                    if (f_unlink((const char *)ftpc.filename) != 0)
                    {
                        printf("\r\nCould not delete.\r\n");
                    }
                    else
                    {
                        printf("\r\nDeleted.\r\n");
                    }
                    gMenuStart = 1;
                    break;
                }
#endif
                else if (gMsgBuf[0] != 0x00)
                {
                    printf("\r\nRetry...\r\n");
                }
            }
        }
        if (gDataSockReady)
        {
            gDataSockReady = 0;
            switch (Command.First)
            {
            case f_dir:
                sprintf((char *)dat, "LIST\r\n");
                send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                break;
            case f_put:
                printf(">put file name?");
                memset(gMsgBuf, 0, sizeof(gMsgBuf));
                scanf("%s", gMsgBuf);
                sprintf((char *)dat, "STOR %s\r\n", gMsgBuf);
                send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                break;
            case f_get:
                printf(">get file name?");
                memset(gMsgBuf, 0, sizeof(gMsgBuf));
                scanf("%s", gMsgBuf);
                sprintf((char *)dat, "RETR %s\r\n", gMsgBuf);
                send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                break;
            default:
                printf("Command.First = default\r\n");
                break;
            }
        }
        if ((size = getSn_SR(socket_ctrl)) > 0)
        { // Don't need to check SOCKERR_BUSY because it doesn't not occur.
            memset(dbuf, 0, _MAX_SS);
            if (size > _MAX_SS)
                size = _MAX_SS - 1;
            ret       = recv(socket_ctrl, dbuf, size);
            dbuf[ret] = '\0';
            if (ret != size)
            {
                if (ret == SOCK_BUSY)
                    return 0;
                if (ret < 0)
                {
                    printf("%d:recv() error:%ld\r\n", socket_ctrl, ret);
                    close(socket_ctrl);
                    return ret;
                }
            }
            printf("Rcvd Command: %s\r\n", dbuf);
            proc_ftpc((char *)dbuf, size);
        }
        break;
    case SOCK_CLOSE_WAIT:
        printf("%d:CloseWait\r\n", socket_ctrl);
        if ((ret = disconnect(socket_ctrl)) != SOCK_OK)
            return ret;
        printf("%d:Closed\r\n", socket_ctrl);
        break;
    case SOCK_CLOSED:
        printf("%d:FTPStart\r\n", socket_ctrl);
        if ((ret = socket(socket_ctrl, Sn_MR_TCP, FTP_destport, 0x0)) != socket_ctrl)
        {
            printf("%d:socket() error:%ld\r\n", socket_ctrl, ret);
            close(socket_ctrl);
            return ret;
        }
        break;
    case SOCK_INIT:
        printf("%d:Opened\r\n", socket_ctrl);
        if ((ret = connect(socket_ctrl, local_ip.cVal, FTP_destport)) != SOCK_OK)
        {
            printf("%d:Connect error\r\n", socket_ctrl);
            return ret;
        }
        connect_state_control_ftpc = 0;
        printf("%d:Connectting...\r\n", socket_ctrl);
        break;
    default:
        break;
    }
    switch (getSn_SR(socket_data))
    {
    case SOCK_ESTABLISHED:
        if (!connect_state_data_ftpc)
        {
            printf("%d:FTP Data socket Connected\r\n", socket_data);
            connect_state_data_ftpc = 1;
        }
        if (gDataPutGetStart)
        {
            switch (Command.Second)
            {
            case s_dir:
                printf("dir waiting...\r\n");
                if ((size = getSn_RX_RSR(socket_data)) > 0)
                { // Don't need to check SOCKERR_BUSY because it doesn't not occur.
                    printf("ok\r\n");
                    memset(dbuf, 0, _MAX_SS);
                    if (size > _MAX_SS)
                        size = _MAX_SS - 1;
                    ret       = recv(socket_data, dbuf, size);
                    dbuf[ret] = '\0';
                    if (ret != size)
                    {
                        if (ret == SOCK_BUSY)
                            return 0;
                        if (ret < 0)
                        {
                            printf("%d:recv() error:%ld\r\n", socket_ctrl, ret);
                            close(socket_data);
                            return ret;
                        }
                    }
                    printf("Rcvd Data:\n\r%s\n\r", dbuf);
                    gDataPutGetStart = 0;
                    Command.Second   = s_nocmd;
                }
                break;
            case s_put:
                printf("put waiting...\r\n");
                if (strlen(ftpc.workingdir) == 1)
                    sprintf(ftpc.filename, "/%s", (uint8_t *)gMsgBuf);
                else
                    sprintf(ftpc.filename, "%s/%s", ftpc.workingdir, (uint8_t *)gMsgBuf);
#if defined(F_FILESYSTEM)
                ftpc.fr = f_open(&(ftpc.fil), (const char *)ftpc.filename, FA_READ);
                if (ftpc.fr == FR_OK)
                {
                    remain_filesize = ftpc.fil.fsize;
                    printf("f_open return FR_OK\r\n");
                    do
                    {
                        memset(dbuf, 0, _MAX_SS);
                        if (remain_filesize > _MAX_SS)
                            send_byte = _MAX_SS;
                        else
                            send_byte = remain_filesize;
                        ftpc.fr = f_read(&(ftpc.fil), (void *)dbuf, send_byte, (UINT *)&blocklen);
                        if (ftpc.fr != FR_OK)
                        {
                            break;
                        }
                        printf("#");
                        send(socket_data, dbuf, blocklen);
                        remain_filesize -= blocklen;
                    } while (remain_filesize != 0);
                    printf("\r\nFile read finished\r\n");
                    ftpc.fr = f_close(&(ftpc.fil));
                }
                else
                {
                    printf("File Open Error: %d\r\n", ftpc.fr);
                    ftpc.fr = f_close(&(ftpc.fil));
                }
#else
                remain_filesize = strlen(ftpc.filename);
                do
                {
                    memset(dbuf, 0, _MAX_SS);
                    blocklen = sprintf((char *)dbuf, "%s", ftpc.filename); // Upload file content
                    printf("########## dbuf:%s\r\n", dbuf);
                    send(socket_data, dbuf, blocklen);
                    remain_filesize -= blocklen;
                } while (remain_filesize != 0);
#endif
                gDataPutGetStart = 0;
                Command.Second   = s_nocmd;
                disconnect(socket_data);
                break;
            case s_get:
                printf("get waiting...\r\n");
                if (strlen(ftpc.workingdir) == 1)
                    sprintf(ftpc.filename, "/%s", (uint8_t *)gMsgBuf);
                else
                    sprintf(ftpc.filename, "%s/%s", ftpc.workingdir, (uint8_t *)gMsgBuf);
#if defined(F_FILESYSTEM)
                ftpc.fr = f_open(&(ftpc.fil), (const char *)ftpc.filename, FA_CREATE_ALWAYS | FA_WRITE);
                if (ftpc.fr == FR_OK)
                {
                    remain_filesize = ftpc.fil.fsize;
                    printf("f_open return FR_OK\r\n");
                    do
                    {
                        memset(dbuf, 0, _MAX_SS);
                        if (remain_filesize > _MAX_SS)
                            send_byte = _MAX_SS;
                        else
                            send_byte = remain_filesize;
                        ftpc.fr = f_read(&(ftpc.fil), (void *)dbuf, send_byte, (UINT *)&blocklen);
                        if (ftpc.fr != FR_OK)
                        {
                            break;
                        }
                        printf("#");
                        send(socket_data, dbuf, blocklen);
                        remain_filesize -= blocklen;
                    } while (remain_filesize != 0);
                    printf("\r\nFile read finished\r\n");
                    ftpc.fr = f_close(&(ftpc.fil));
                }
                else
                {
                    printf("File Open Error: %d\r\n", ftpc.fr);
                    ftpc.fr = f_close(&(ftpc.fil));
                }
#else
                remain_filesize = strlen(ftpc.filename);
                do
                {
                    memset(dbuf, 0, _MAX_SS);
                    blocklen = sprintf((char *)dbuf, "%s", ftpc.filename); // Upload file content
                    printf("########## dbuf:%s\r\n", dbuf);
                    send(socket_data, dbuf, blocklen);
                    remain_filesize -= blocklen;
                } while (remain_filesize != 0);
#endif
                gDataPutGetStart = 0;
                Command.Second   = s_nocmd;
                disconnect(socket_data);
                break;
            case s_get:
                printf("get waiting...\r\n");
                if (strlen(ftpc.workingdir) == 1)
                    sprintf(ftpc.filename, "/%s", (uint8_t *)gMsgBuf);
                else
                    sprintf(ftpc.filename, "%s/%s", ftpc.workingdir, (uint8_t *)gMsgBuf);
#if defined(F_FILESYSTEM)
                ftpc.fr = f_open(&(ftpc.fil), (const char *)ftpc.filename, FA_CREATE_ALWAYS | FA_WRITE);
                if (ftpc.fr == FR_OK)
                {
                    printf("f_open return FR_OK\r\n");
                    while (1)
                    {
                        if ((remain_datasize = getSn_RX_RSR(socket_data)) > 0)
                        {
                            while (1)
                            {
                                memset(dbuf, 0, _MAX_SS);
                                if (remain_datasize > _MAX_SS)
                                    recv_byte = _MAX_SS;
                                else
                                    recv_byte = remain_datasize;
                                ret              = recv(socket_data, dbuf, recv_byte);
                                ftpc.fr          = f_write(&(ftpc.fil), (const void *)dbuf, (UINT)ret, (UINT *)&blocklen);
                                remain_datasize -= blocklen;
                                if (ftpc.fr != FR_OK)
                                {
                                    printf("f_write failed\r\n");
                                    break;
                                }
                                if (remain_datasize <= 0)
                                    break;
                            }
                            if (ftpc.fr != FR_OK)
                            {
                                printf("f_write failed\r\n");
                                break;
                            }
                            printf("#");
                        }
                        else
                        {
                            if (getSn_SR(socket_data) != SOCK_ESTABLISHED)
                                break;
                        }
                    }
                    printf("\r\nFile write finished\r\n");
                    ftpc.fr          = f_close(&(ftpc.fil));
                    gDataPutGetStart = 0;
                }
                else
                {
                    printf("File Open Error: %d\r\n", ftpc.fr);
                }
#else
                while (1)
                {
                    if ((remain_datasize = getSn_RX_RSR(socket_data)) > 0)
                    {
                        while (1)
                        {
                            memset(dbuf, 0, _MAX_SS);
                            if (remain_datasize > _MAX_SS)
                                recv_byte = _MAX_SS;
                            else
                                recv_byte = remain_datasize;
                            ret = recv(socket_data, dbuf, recv_byte);
                            printf("########## dbuf:%s\r\n", dbuf);
                            remain_datasize -= ret;
                            if (remain_datasize <= 0)
                                break;
                        }
                    }
                    else
                    {
                        if (getSn_SR(socket_data) != SOCK_ESTABLISHED)
                            break;
                    }
                }
                gDataPutGetStart = 0;
                Command.Second   = s_nocmd;
#endif
                break;
            default:
                printf("Command.Second = default\r\n");
                break;
            }
        }
        break;
    case SOCK_CLOSE_WAIT:
        printf("%d:CloseWait\r\n", socket_data);
        if ((size = getSn_RX_RSR(socket_data)) > 0)
        { // Don't need to check SOCKERR_BUSY because it doesn't not occur.
            ret       = recv(socket_data, dbuf, size);
            dbuf[ret] = '\0';
            if (ret != size)
            {
                if (ret == SOCK_BUSY)
                    return 0;
                if (ret < 0)
                {
                    printf("%d:recv() error:%ld\r\n", socket_ctrl, ret);
                    close(socket_data);
                    return ret;
                }
            }
            printf("Rcvd Data:\n\r%s\n\r", dbuf);
        }
        if ((ret = disconnect(socket_data)) != SOCK_OK)
            return ret;
        printf("%d:Closed\r\n", socket_data);
        break;
    case SOCK_CLOSED:
        if (ftpc.dsock_state == DATASOCK_READY)
        {
            if (ftpc.dsock_mode == PASSIVE_MODE)
            {
                printf("%d:FTPDataStart, port : %d\r\n", socket_data, local_port);
                if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data)
                {
                    printf("%d:socket() error:%ld\r\n", socket_data, ret);
                    close(socket_data);
                    return ret;
                }
                local_port++;
                if (local_port > 50000)
                    local_port = 35000;
            }
            else
            {
                printf("%d:FTPDataStart, port : %d\r\n", socket_data, local_port);
                if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data)
                {
                    printf("%d:socket() error:%ld\r\n", socket_data, ret);
                    close(socket_data);
                    return ret;
                }
                local_port++;
                if (local_port > 50000)
                    local_port = 35000;
            }
            ftpc.dsock_state = DATASOCK_START;
        }
        break;
  
    case SOCK_INIT:
        printf("%d:Opened\r\n", socket_data);
        if (ftpc.dsock_mode == ACTIVE_MODE)
        {
            if ((ret = listen(socket_data)) != SOCK_OK)
            {
                printf("%d:Listen error\r\n", socket_data);
                return ret;
            }
            gDataSockReady = 1;
            printf("%d:Listen ok\r\n", socket_data);
        }
        else
        {
            if ((ret = connect(socket_data, remote_ip.cVal, remote_port)) != SOCK_OK)
            {
                printf("%d:Connect error\r\n", socket_data);
                return ret;
            }
            gDataSockReady = 1;
        }
        connect_state_data_ftpc = 0;
        break;
    default:
        break;
    }
    return 0;
}

在这个函数中,会分别执行两个TCP状态机,一个用于FTP控制,一个用于FTP数据传输,FTP数据传输的状态机,仅当要进行数据传输时(上传或下载文件)才会开启。

首先看到FTP控制的状态机,大致流程如下图所示:

当用户在选项菜单中选择不同的选项时,系统会根据所选选项发送相应的操作指令。这些操作指令将被发送至服务器,随后服务器会返回相应的响应内容。在proc_ftpc()函数中,程序会根据服务器返回的响应码来判断操作是否成功,并依据响应码的具体情况执行下一步操作。一旦相应操作完成,程序将自动返回选项菜单,以便用户继续进行后续操作。

9 运行结果

烧录例程运行后,首先进行了PHY链路检测,然后是通过DHCP获取网络地址并打印网络地址信息,打印出FTP连接信息,并提示输入信息进行连接,如下图所示:

FileZilla Server Interface 是 FileZilla Server 的图形用户界面(GUI),用于管理和配置 FileZilla Server。FileZilla Server 是一款开源的、跨平台的 FTP 和 FTP over TLS (FTPS) 服务器,它常用于文件共享和数据传输。下载链接:服务端 - FileZilla中文网。下载、安装完成后打开。

第一步:建立服务器,设置一个账户(用户名:wiznet 密码:123456)

第二步:添加一个共享文件夹,用于客户端和服务器的文件操作使用

第三步:在串口助手界面根据串口消息提示Rcvd Command: 220-FileZilla Server ????????? 0.9.60 beta先输入用户名,再输入密码(注意:发送的内容的结尾需要带上回车换行符),然后串口就会发送6条选项

第四步:我们选择6,从服务器获取文件

第五步:然后输入需要获取文件的名字,如图所示,成功获取文件内容

其他操作类似,这里就不在一一进行讲解。

10 总结

本文讲解了如何在 W55MH32 芯片上实现 FTP 协议的客户端模式,通过实战例程展示了使用该客户端模式访问 FTP 服务器并下载文件的过程,涵盖 FTP 客户端模式初始化、在主循环中运行相关函数实现与服务器交互等关键步骤。文章详细介绍了 FTP 协议的概念、特点、应用场景、工作流程、主动与被动模式、客户端功能、报文解析,帮助读者理解其在文件传输中的实际应用价值。

下一篇文章将聚焦 WOL(Wake-on-LAN)网络唤醒功能,解析其核心原理及在网络设备管理中的应用,同时讲解如何在W55MH32上实现 WOL 功能,敬请期待!

### STM32W55BCGU6低功耗特性与配置 #### 一、STM32W55BCGU6概述 STM32系列微控制器以其高性能和丰富的外设而闻名,其中STM32W55BCGU6型号继承了该家族的优势并特别注重低功耗应用的设计。此款MCU不仅具备强大的处理能力,而且通过多种机制实现了高效的能量管理,在休眠模式下仍能维持必要的外围设备运作。 #### 二、低功耗特性 为了满足不同应用场景的需求,STM32W55BCGU6提供了多样化的省电选项: - **待机模式(Standby Mode)**:当系统处于长时间无活动状态时可启用这种最深程度的节能方式;此时除了RTC之外几乎所有的电路都会被关闭以减少静态电流消耗。 - **停止模式(STOP Mode)**:允许CPU暂停工作但保留SRAM中的数据以及一些基本定时器的功能,以便快速唤醒恢复执行任务。在此期间可以利用外部中断或事件来触发重新激活过程[^4]。 - **睡眠模式(Sleep Mode)**:这是相对较浅的一种节省电量的方法,核心频率降低至最低限度但仍保持足够的响应速度用于即时处理突发情况下的请求。 #### 三、具体配置方法 针对上述提到的各种低功率运行状况,可以通过调整软件设置来进行优化控制: 1. 对于进入STOP模式的情况,开发者可以根据实际需求选择合适的入口函数(`PWR_STOPEntry_WFI` 或 `PWR_STOPEntry_WFE`) 来决定等待条件是特定类型的中断还是任意发生的事件。 ```c if(PWR_STOPEntry == PWR_STOPEntry_WFI) { /* Request Wait For Interrupt */ __WFI(); } else { /* Request Wait For Event */ __WFE(); } ``` 2. 当涉及到更深层次如Standby模式,则需调用相应API接口完成电源管理和实时时钟(RTC)初始化等工作,并确保在此之前保存好所有重要变量的状态信息以防丢失。 3. 此外还有其他参数可供调节比如电压规模(VOS),主PLL分频系数等都对最终达到的效果有着直接影响,因此建议参照官方文档详细了解各项指令的具体含义及其作用范围后再做设定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值