第十九章 W55MH32 FTP_Client示例

描述

单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机

W55MH32是WIZnet重磅推出的高性能以太网单片机,它为用户带来前所未有的集成化体验。这颗芯片将强大的组件集于一身,具体来说,一颗W55MH32内置高性能Arm® Cortex-M3核心,其主频最高可达216MHz;配备1024KB FLASH与96KB SRAM,满足存储与数据处理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP协议栈、内置MAC以及PHY,拥有独立的32KB以太网收发缓存,可供8个独立硬件socket使用。如此配置,真正实现了All-in-One解决方案,为开发者提供极大便利。

在封装规格上,W55MH32 提供了两种选择:QFN100和QFN68。

W55MH32L采用QFN100封装版本,尺寸为12x12mm,其资源丰富,专为各种复杂工控场景设计。它拥有66个GPIO、3个ADC、12通道DMA、17个定时器、2个I2C、5个串口、2个SPI接口(其中1个带I2S接口复用)、1个CAN、1个USB2.0以及1个SDIO接口。如此丰富的外设资源,能够轻松应对工业控制中多样化的连接需求,无论是与各类传感器、执行器的通信,还是对复杂工业协议的支持,都能游刃有余,成为复杂工控领域的理想选择。 同系列还有QFN68封装的W55MH32Q版本,该版本体积更小,仅为8x8mm,成本低,适合集成度高的网关模组等场景,软件使用方法一致。更多信息和资料请进入http://www.w5500.com/网站或者私信获取。

此外,本W55MH32支持硬件加密算法单元,WIZnet还推出TOE+SSL应用,涵盖TCP SSL、HTTP SSL以及 MQTT SSL等,为网络通信安全再添保障。

为助力开发者快速上手与深入开发,基于W55MH32L这颗芯片,WIZnet精心打造了配套开发板。开发板集成WIZ-Link芯片,借助一根USB C口数据线,就能轻松实现调试、下载以及串口打印日志等功能。开发板将所有外设全部引出,拓展功能也大幅提升,便于开发者全面评估芯片性能。

若您想获取芯片和开发板的更多详细信息,包括产品特性、技术参数以及价格等,欢迎访问官方网页:http://www.w5500.com/,我们期待与您共同探索W55MH32的无限可能。

Client

第十九章 W55MH32 FTP_Client示例

本篇文章,我们将详细介绍如何在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协议特点

基于 TCP 传输:

FTP 使用两个 TCP 连接:控制连接(端口 21)和数据连接(端口 20 或 PASV 模式动态分配的端口),确保可靠的数据传输。

分离控制与数据:

控制连接用于发送命令和接收响应。

数据连接用于文件内容或目录信息的传输。

支持多种传输模式:

主动模式(Active Mode):服务器主动连接客户端的数据端口。

被动模式(Passive Mode):客户端主动连接服务器提供的数据端口,解决 NAT 防火墙限制。

支持多种文件操作:

文件上传(STOR)、下载(RETR)、删除(DELE)。

目录操作(MKD、RMD、CWD、PWD)。

获取文件列表(LIST、NLST)。

明文传输(传统 FTP):

用户名、密码及数据以明文形式传输,不安全。

安全改进:FTPS(FTP Secure,基于 SSL/TLS)和 SFTP(Secure File Transfer Protocol,基于 SSH)。

灵活的用户认证机制:

支持匿名登录(匿名用户可通过 email 作为密码)。

支持认证用户名和密码。

3 FTP协议应用场景

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

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

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

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

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

4 FTP工作流程

1. 建立控制连接

客户端初始化:客户端启动 FTP 客户端程序,指定要连接的 FTP 服务器的地址和端口号(端口号为 21)。

TCP 连接建立:客户端通过 TCP 协议向服务器的 21 端口发起连接请求。服务器监听该端口,接收到请求后,与客户端建立起一条 TCP 连接,这个连接被称为控制连接,主要用于传输 FTP 命令和服务器的响应信息。

身份验证:连接建立后,服务器会提示客户端输入用户名和密码进行身份验证。客户端发送相应的用户名和密码信息到服务器,服务器验证通过后,才允许客户端进行后续操作。也有一些匿名 FTP 服务器,允许用户以 “anonymous” 作为用户名,以电子邮件地址作为密码进行登录,提供公开的文件访问服务。

2. 传输模式选择

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

主动模式(PORT 模式):客户端通过控制连接告诉服务器自己的数据端口(客户端随机开放的一个端口),服务器使用 20 端口主动连接客户端的数据端口来传输数据。

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

3. 数据传输

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

上传文件:客户端向服务器发送 STOR(存储)命令,然后通过数据连接将本地文件数据发送到服务器。服务器接收到数据后,将其存储在指定的目录下。

下载文件:客户端向服务器发送 RETR(检索)命令,请求下载服务器上的文件。服务器通过数据连接将文件数据发送给客户端,客户端接收数据并将其保存到本地指定位置。

目录操作:客户端还可以发送诸如 LIST(列出目录内容)、CWD(更改工作目录)、MKD(创建目录)、RMD(删除目录)等命令,服务器执行相应操作,并通过控制连接返回操作结果。执行这些命令时,若需要传输目录列表等数据,也会通过数据连接进行传输。

4. 关闭连接

数据连接关闭:在完成文件传输或其他操作后,数据连接会被关闭。如果还有其他操作需要进行,客户端和服务器可以根据需要重新建立数据连接。

控制连接关闭:当客户端完成所有操作后,会向服务器发送 QUIT 命令,服务器接收到该命令后,会关闭控制连接。至此,客户端与服务器之间的 FTP 会话结束。

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

主动模式(Active Mode):

客户端打开一个端口并监听。

客户端通过控制连接告诉服务器自己的 IP 和端口。

服务器主动连接到客户端指定的端口传输数据。

被动模式(Passive Mode):

客户端通过控制连接请求被动模式。

服务器打开一个随机端口并通过控制连接告知客户端。

客户端主动连接到服务器指定的端口传输数据。

优缺点对比:

主动模式更适合在服务器端网络无防火墙限制的环境。

被动模式更适合客户端在 NAT 或防火墙后的情况。

6 FTP客户端实现功能

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

连接到 FTP 服务器。

用户身份认证。

上传和下载文件。

浏览和管理服务器目录结构。

支持主动模式和被动模式的切换。

提供断点续传功能(某些高级客户端)。

7 FTP报文解析

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

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

<命令>:FTP命令(如 USER、PASS)。

<参数>:命令的附加信息(如用户名、文件名)。

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

以下是 FTP 常见命令:

USER: 提供用户名进行身份验证。

PASS: 提供密码进行身份验证。

CWD: 更改当前工作目录。

PWD: 显示当前工作目录。

LIST: 列出目录下的文件和子目录。

RETR: 从服务器下载文件。

STOR: 上传文件到服务器。

DELE: 删除指定文件。

MKD: 创建新目录。

RMD: 删除目录。

QUIT: 终止会话并退出。

TYPE: 设置文件传输类型(ASCII 或 Binary)。

PORT: 指定数据连接的端口。

PASV: 启用被动模式,服务器指定端口供客户端连接。

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

<状态码>:三位数字表示状态。

<说明文字>:状态的文字描述。

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

1xx(信息性响应): 主要是提供一些初步的信息,通常表示服务器正在处理请求,还没有完成操作。

2xx(成功响应): 表示命令成功执行。这是客户端最希望看到的响应类型之一,说明请求的操作(如登录、文件传输等)顺利完成。

3xx(补充信息响应): 表示服务器需要一些额外的信息才能完成操作。通常是在身份验证或者文件定位等过程中出现。

4xx(暂时错误响应): 表示客户端的请求有问题,但错误是暂时的,可能通过一些调整(如重新发送请求等)可以解决。

5xx(永久性错误响应): 表示客户端的请求存在错误,并且这个错误是比较严重的,很难通过简单的调整来纠正。

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

客户端建立TCP连接到服务器的21端口

服务器返回:220 Welcome to FTP Serverrn

客户端发送:USER wiznetrn

服务器返回:331 User wiznet OK.Password requiredrn

客户端发送:PASS wiznetrn

服务器返回:230 User logged inrn

客户端发送PORT 192,168,1,5,20,100rn(主动模式,192,168,1,5是客户端的地址,20,100是客户端期望的端口号20*256+100=5260)

服务器返回:200 PORT command successfulrn

客户端发送:LISTrn(DIR命令,获取当前目录的文件信息)

服务器回复:150 Opening ASCII mode data connection for file listrn

服务器像客户端期望的端口号发起TCP连接,并传输目录信息,传输完成后关闭TCP连接。

客户端发送:QUITrn(退出FTP会话)

服务器回复:221 Goodbyern

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 Connectedrn", socket_ctrl);
           strcpy(ftpc.workingdir, "/");
           connect_state_control_ftpc = 1;
       }
       if (gMenuStart)
       {
           gMenuStart = 0;
           printf("rn----------------------------------------rn");
           printf("Press menu keyrn");
           printf("----------------------------------------rn");
           printf("1 > View FTP Server Directoryrn");
           printf("2 > View My Directoryrn");
           printf("3 > Sets the type of file to be transferred. Current state : %srn", (ftpc.type == ASCII_TYPE) ? "Ascii" : "Binary");
           printf("4 > Sets Data Connection. Current state : %srn", (ftpc.dsock_mode == ACTIVE_MODE) ? "Active" : "Passive");
           printf("5 > Put File to Serverrn");
           printf("6 > Get File from Serverrn");
#if defined(F_FILESYSTEM)
           printf("7 > Delete My Filern");
#endif
           printf("----------------------------------------rn");
           while (1)
           {
               memset(gMsgBuf, 0, sizeof(gMsgBuf));
               scanf("%s", gMsgBuf);
               if (gMsgBuf[0] == '1')
               {
                   if (ftpc.dsock_mode == PASSIVE_MODE)
                   {
                       sprintf((char *)dat, "PASVrn");
                       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,%drn", 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, "PASVrn");
                       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,%drn", 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, "PASVrn");
                       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,%drn", 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("rn%srn", 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.Binrn-rwxr-xr-x 1 ftp ftp 512 Dec 31 2014 test.txtrn");
                   printf("rn%srn", dbuf);
#endif
                   gMenuStart = 1;
                   break;
               }
               else if (gMsgBuf[0] == '3')
               {
                   printf("1 > ASCIIrn");
                   printf("2 > BINARYrn");
                   while (1)
                   {
                       memset(gMsgBuf, 0, sizeof(gMsgBuf));
                       scanf("%s", gMsgBuf);
                       if (gMsgBuf[0] == '1')
                       {
                           sprintf((char *)dat, "TYPE %crn", TransferAscii);
                           ftpc.type = ASCII_TYPE;
                           send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                           break;
                       }
                       else if (gMsgBuf[0] == '2')
                       {
                           sprintf((char *)dat, "TYPE %crn", TransferBinary);
                           ftpc.type = IMAGE_TYPE;
                           send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
                           break;
                       }
                       else if (gMsgBuf[0] != 0x00)
                       {
                           printf("rnRetry...rn");
                       }
                   }
                   break;
               }
               else if (gMsgBuf[0] == '4')
               {
                   printf("1 > ACTIVErn");
                   printf("2 > PASSIVErn");
                   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("rnRetry...rn");
                       }
                   }
                   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 %srn", gMsgBuf);
                   if (f_unlink((const char *)ftpc.filename) != 0)
                   {
                       printf("rnCould not delete.rn");
                   }
                   else
                   {
                       printf("rnDeleted.rn");
                   }
                   gMenuStart = 1;
                   break;
               }
#endif
               else if (gMsgBuf[0] != 0x00)
               {
                   printf("rnRetry...rn");
               }
           }
       }
       if (gDataSockReady)
       {
           gDataSockReady = 0;
           switch (Command.First)
           {
           case f_dir:
               sprintf((char *)dat, "LISTrn");
               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 %srn", 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 %srn", gMsgBuf);
               send(socket_ctrl, (uint8_t *)dat, strlen((char *)dat));
               break;
           default:
               printf("Command.First = defaultrn");
               break;
           }
       }
       if ((size = getSn_RX_RSR(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] = '';
           if (ret != size)
           {
               if (ret == SOCK_BUSY)
                   return 0;
               if (ret < 0)
               {
                   printf("%d:recv() error:%ldrn", socket_ctrl, ret);
                   close(socket_ctrl);
                   return ret;
               }
           }
           printf("Rcvd Command: %srn", dbuf);
           proc_ftpc((char *)dbuf, size);
       }
       break;
   case SOCK_CLOSE_WAIT:
       printf("%d:CloseWaitrn", socket_ctrl);
       if ((ret = disconnect(socket_ctrl)) != SOCK_OK)
           return ret;
       printf("%d:Closedrn", socket_ctrl);
       break;
   case SOCK_CLOSED:
       printf("%d:FTPStartrn", socket_ctrl);
       if ((ret = socket(socket_ctrl, Sn_MR_TCP, FTP_destport, 0x0)) != socket_ctrl)
       {
           printf("%d:socket() error:%ldrn", socket_ctrl, ret);
           close(socket_ctrl);
           return ret;
       }
       break;
   case SOCK_INIT:
       printf("%d:Openedrn", socket_ctrl);
       if ((ret = connect(socket_ctrl, local_ip.cVal, FTP_destport)) != SOCK_OK)
       {
           printf("%d:Connect errorrn", socket_ctrl);
           return ret;
       }
       connect_state_control_ftpc = 0;
       printf("%d:Connectting...rn", socket_ctrl);
       break;
   default:
       break;
   }
   switch (getSn_SR(socket_data))
   {
   case SOCK_ESTABLISHED:
       if (!connect_state_data_ftpc)
       {
           printf("%d:FTP Data socket Connectedrn", socket_data);
           connect_state_data_ftpc = 1;
       }
       if (gDataPutGetStart)
       {
           switch (Command.Second)
           {
           case s_dir:
               printf("dir waiting...rn");
               if ((size = getSn_RX_RSR(socket_data)) > 0)
               { // Don't need to check SOCKERR_BUSY because it doesn't not occur.
                   printf("okrn");
                   memset(dbuf, 0, _MAX_SS);
                   if (size > _MAX_SS)
                       size = _MAX_SS - 1;
                   ret       = recv(socket_data, dbuf, size);
                   dbuf[ret] = '';
                   if (ret != size)
                   {
                       if (ret == SOCK_BUSY)
                           return 0;
                       if (ret < 0)
                       {
                           printf("%d:recv() error:%ldrn", socket_ctrl, ret);
                           close(socket_data);
                           return ret;
                       }
                   }
                   printf("Rcvd Data:nr%snr", dbuf);
                   gDataPutGetStart = 0;
                   Command.Second   = s_nocmd;
               }
               break;
           case s_put:
               printf("put waiting...rn");
               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_OKrn");
                   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("rnFile read finishedrn");
                   ftpc.fr = f_close(&(ftpc.fil));
               }
               else
               {
                   printf("File Open Error: %drn", 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:%srn", 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...rn");
               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_OKrn");
                   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 failedrn");
                                   break;
                               }
                               if (remain_datasize <= 0)
                                   break;
                           }
                           if (ftpc.fr != FR_OK)
                           {
                               printf("f_write failedrn");
                               break;
                           }
                           printf("#");
                       }
                       else
                       {
                           if (getSn_SR(socket_data) != SOCK_ESTABLISHED)
                               break;
                       }
                   }
                   printf("rnFile write finishedrn");
                   ftpc.fr          = f_close(&(ftpc.fil));
                   gDataPutGetStart = 0;
               }
               else
               {
                   printf("File Open Error: %drn", 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:%srn", 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 = defaultrn");
               break;
           }
       }
       break;
   case SOCK_CLOSE_WAIT:
       printf("%d:CloseWaitrn", 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] = '';
           if (ret != size)
           {
               if (ret == SOCK_BUSY)
                   return 0;
               if (ret < 0)
               {
                   printf("%d:recv() error:%ldrn", socket_ctrl, ret);
                   close(socket_data);
                   return ret;
               }
           }
           printf("Rcvd Data:nr%snr", dbuf);
       }
       if ((ret = disconnect(socket_data)) != SOCK_OK)
           return ret;
       printf("%d:Closedrn", socket_data);
       break;
   case SOCK_CLOSED:
       if (ftpc.dsock_state == DATASOCK_READY)
       {
           if (ftpc.dsock_mode == PASSIVE_MODE)
           {
               printf("%d:FTPDataStart, port : %drn", socket_data, local_port);
               if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data)
               {
                   printf("%d:socket() error:%ldrn", socket_data, ret);
                   close(socket_data);
                   return ret;
               }
               local_port++;
               if (local_port > 50000)
                   local_port = 35000;
           }
           else
           {
               printf("%d:FTPDataStart, port : %drn", socket_data, local_port);
               if ((ret = socket(socket_data, Sn_MR_TCP, local_port, 0x0)) != socket_data)
               {
                   printf("%d:socket() error:%ldrn", 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:Openedrn", socket_data);
       if (ftpc.dsock_mode == ACTIVE_MODE)
       {
           if ((ret = listen(socket_data)) != SOCK_OK)
           {
               printf("%d:Listen errorrn", socket_data);
               return ret;
           }
           gDataSockReady = 1;
           printf("%d:Listen okrn", socket_data);
       }
       else
       {
           if ((ret = connect(socket_data, remote_ip.cVal, remote_port)) != SOCK_OK)
           {
               printf("%d:Connect errorrn", socket_data);
               return ret;
           }
           gDataSockReady = 1;
       }
       connect_state_data_ftpc = 0;
       break;
   default:
       break;
   }
   return 0;
}
 

 

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

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

Client

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

9 运行结果

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

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

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

ClientClient

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

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

Client

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

Client

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

Client

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

10 总结

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

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

WIZnet 是一家无晶圆厂半导体公司,成立于 1998 年。产品包括互联网处理器 iMCU™,它采用 TOE(TCP/IP 卸载引擎)技术,基于独特的专利全硬连线 TCP/IP。iMCU™ 面向各种应用中的嵌入式互联网设备。

WIZnet 在全球拥有 70 多家分销商,在香港、韩国、美国设有办事处,提供技术支持和产品营销。

香港办事处管理的区域包括:澳大利亚、印度、土耳其、亚洲(韩国和日本除外)。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分