全部例程

W6300作为FTP客户端

W6300

更新于 2026年10月19日

本篇总结

W6300 实现 FTP 客户端功能,可访问 FTP 服务器进行文件上传、下载和目录管理。

🔑 MH2457、QSPI、UART调试、硬件TOE引擎、FTP、DNS、DHCP

硬件平台:本例程基于 W6300-EVB-M4 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

本示例演示如何在 W6300 上实现 FTP(文件传输协议)的客户端模式,通过实战例程讲解 FTP 文件上传、下载和目录管理操作。该例程用到的其他网络协议,例如 DHCP,请参考相关章节。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • FileZilla Server:FTP 服务器软件,用于在 PC 端搭建 FTP 服务器:点击下载
  • WIZ UartTool V1.0:串口调试工具:点击下载

FTP 协议简介

FTP(File Transfer Protocol,文件传输协议)是应用层的文件传输协议,采用客户端-服务器架构,基于 TCP 双连接工作:控制连接(端口 21)用于传输命令和响应,数据连接(端口 20 或 PASV 模式动态端口)用于传输文件数据。FTP 支持主动模式(PORT)和被动模式(PASV),提供用户认证、文件上传/下载、目录操作等功能。响应码以三位数字表示,1xx 为信息性响应、2xx 为成功、3xx 需补充信息、4xx 为暂时错误、5xx 为永久错误。

命令说明
USER提供用户名进行身份验证
PASS提供密码进行身份验证
RETR从服务器下载文件
STOR上传文件到服务器
LIST列出目录下的文件和子目录
CWD更改当前工作目录
PWD显示当前工作目录
DELE删除指定文件
MKD创建新目录
PASV启用被动模式
PORT指定数据连接端口(主动模式)
QUIT终止会话并退出

W6300 的优势

W6300 SDK 内置了完整的 FTP 客户端库(ftpc.c/h),命令组包/解包、状态机管理、文件操作全部由库函数处理。你只需要:调用 ftpc_init() 初始化客户端 → 在主循环中调用 ftpc_run() 驱动双状态机。

FTP 获取目录的报文交互示例

客户端                                服务器
  |                                     |
  |====== TCP 连接到 21 端口 ==========>|  建立控制连接
  |<----- 220 Welcome to FTP Server ----|
  |------ USER wiznet ----------------->|
  |<----- 331 Password required --------|
  |------ PASS wiznet ----------------->|
  |<----- 230 User logged in -----------|
  |------ PORT 192,168,1,5,20,100 ----->|  主动模式, 端口 = 20*256+100 = 5260
  |<----- 200 PORT command successful --|
  |------ LIST ------------------------>|  获取当前目录文件信息
  |<----- 150 Opening data connection --|
  |<====== 目录数据 (数据连接传输) ====|
  |------ QUIT ------------------------>|  退出 FTP 会话
  |<----- 221 Goodbye ------------------|

硬件连接与拓扑

请注意:

PC 和 W6300 需处于同一网段。PC 端需先启动 FileZilla Server 并配置好用户账户和共享文件夹,W6300 作为 FTP 客户端连接服务器进行文件操作。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口
  3. 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
┌──────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬───────────────────┬──────────┘
│ │
┌────┴──────┐ ┌────┴──────────────┐
│ W6300 │ │ PC │
│(FTP Cli) │ │ (FileZilla Srv) │
│192.168.1.8│ │ TCP :21 │
└────┬──────┘ └───────────────────┘
│ QSPI ↑
┌────┴──────┐ DEBUG USB
│ MH2457 │──────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:PC 运行 FileZilla Server 作为 FTP 服务器(TCP :21),W6300 作为 FTP 客户端连接,MH2457 通过 QSPI 驱动 W6300

工程结构

工程核心文件如下:

Project/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c           ← 网络地址定义 + FTP 服务器 IP + user_run() 主流程
    ├── ftpc.c                ← FTP 客户端实现(初始化/运行/命令与数据传输)
    ├── ftpc.h
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    └── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器

本例程的核心:通过 ftpc_init 初始化客户端配置,ftpc_run 驱动控制连接与数据连接两个状态机,ftp_client_download 处理命令与数据收发。

步骤1:FTP Client 初始化

文件:USER/ftpc.c — ftpc_init() 函数的主要作用是初始化 FTP 客户端的配置和状态,包括设置服务器 IP 地址、控制 Socket 和数据 Socket、本地端口范围、传输模式,并标记客户端为待连接状态。

void ftpc_init(uint8_t *src_ip, uint8_t sn_ctrl, uint8_t sn_data)
{
    if (src_ip != NULL)
    {
        memcpy(s_ftpc_server_ip, src_ip, sizeof(s_ftpc_server_ip));
    }
    s_ftpc_control_socket = sn_ctrl;
    s_ftpc_data_socket = sn_data;
    s_ftpc_data_local_port = FTPC_DATA_LOCAL_PORT_START;
    s_ftpc_type = FTPC_ASCII_TYPE;
    s_ftpc_data_mode = FTPC_ACTIVE_MODE;
    s_ftpc_pending = 1U;
}

在 user_main.c 里传入服务器 IP 和两个 Socket 编号:

uint8_t ftp_ip[4] = {192, 168, 1, 20};

ftpc_init(ftp_ip, SOCKET_CTRL, SOCKET_DATA);

步骤2:在主循环中运行 ftpc_run

文件:USER/user_main.c — 在主循环中不断调用 ftpc_run(),由它完成控制连接的建立、登录、菜单交互以及数据连接的收发。

while (1)
{
    ftpc_run(ethernet_buf);
}

ftpc_run() 内部会调用 ftpc_open_connection() 建立控制连接,调用 ftpc_login() 完成用户认证,最后进入 ftpc_menu() 菜单循环。

步骤3:FTP 控制连接与命令交互

文件:USER/ftpc.c — ftpc_menu() 通过串口菜单驱动所有 FTP 操作,用户选择菜单项后,程序组包对应命令发送给服务器,服务器返回三位响应码,由 ftpc_read_reply() 解析并做相应处理。

static void ftpc_menu(void)
{
    while (getSn_SR(s_ftpc_control_socket) == SOCK_ESTABLISHED)
    {
        uint8_t key;
        int result = 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",
               (s_ftpc_type == FTPC_ASCII_TYPE) ? "Ascii" : "Binary");
        printf("4> Sets Data Connection. Current state : %s\r\n",
               (s_ftpc_data_mode == FTPC_ACTIVE_MODE) ? "Active" : "Passive");
        printf("5> Put File to Server\r\n");
        printf("6> Get File from Server\r\n");
        printf("----------------------------------------\r\n");

        key = ftpc_get_char();
        printf("%c\r\n", (int)key);

        if (key == '1')
        {
            result = ftpc_do_list();
        }
        else if (key == '2')
        {
            printf("\r\ndrwxr-xr-x 1 ftp ftp 0 Dec 31 2014 $Recycle.Bin\r\n");
            printf("-rwxr-xr-x 1 ftp ftp 512 Dec 31 2014 test.txt\r\n");
        }
        else if (key == '3')
        {
            /* 切换 ASCII / BINARY 传输类型 */
        }
        else if (key == '4')
        {
            /* 切换 ACTIVE / PASSIVE 数据连接模式 */
        }
        else if (key == '5')
        {
            result = ftpc_do_put();
        }
        else if (key == '6')
        {
            result = ftpc_do_get();
        }
        else
        {
            printf("Retry...\r\n");
        }

        if (result < 0)
        {
            printf("FTP operation failed.\r\n");
        }
    }
}

ftpc_read_reply() 在控制连接上按行读取服务器响应,识别形如 230 这样的三位状态码并返回,供上层判断成功与否。

步骤4:FTP 数据连接与文件传输

文件:USER/ftpc.c — 数据连接由 ftpc_prepare_data() 建立。主动模式下客户端先 socket() + listen() 本地端口并发送 PORT;被动模式下客户端发送 PASV 并主动 connect() 服务器指定的端口。

static int ftpc_wait_active_established(void)
{
    uint32_t start_tick = wiz_platform_get_tick();

    if (s_ftpc_data_mode != FTPC_ACTIVE_MODE)
    {
        return 0;
    }

    while ((uint32_t)(wiz_platform_get_tick() - start_tick) < FTPC_CONNECT_TIMEOUT_MS)
    {
        uint8_t status = getSn_SR(s_ftpc_data_socket);

        if (status == SOCK_ESTABLISHED)
        {
            return 0;
        }

        if (status == SOCK_CLOSE_WAIT)
        {
            /*
             * 服务器可能在客户端进入此函数之前就已经把数据发完并发 FIN,
             * 此时 Socket 已进入 SOCK_CLOSE_WAIT。如果 RX 缓冲区里还有数据,
             * 必须返回成功,让 ftpc_receive_data() 把数据读出来。
             */
            if (getSn_RX_RSR(s_ftpc_data_socket) > 0U)
            {
                return 0;
            }

            return -1;
        }

        if (status == SOCK_CLOSED)
        {
            return -1;
        }

        wiz_platform_delay_ms(1U);
    }

    return -1;
}

ftpc_receive_data() 在数据 socket 上循环读取数据,直到 socket 进入 SOCK_CLOSE_WAIT 或 SOCK_CLOSED 且缓冲区为空,然后关闭数据 socket 返回。对下载(RETR)和目录列表(LIST)都走这条路径。

运行验证

烧录例程运行后,首先进行 PHY 链路检测,然后通过 DHCP 获取网络地址并打印网络地址信息,随后连接 FTP 服务器并提示输入用户名和密码。

MH2457 W6300 QSPI example start
W6300 FTP Client example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.8
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================

FTP client target: 192.168.1.20:21
1:FTP Connected
Rcvd Command: 220-FileZilla Server ????????? 0.9.60 beta
Rcvd Command: 220-written by Tim Kosse ([email protected])
Rcvd Command: 220 Please visit https://filezilla-project.org/

Input your User ID > wiznet
Rcvd Command: 331 Password required for wiznet

Input your Password > 123456
Rcvd Command: 230 Logged on

User logged in, proceed
Rcvd Command: 200 Type set to A

----------------------------------------
Press menu key
----------------------------------------
1> View FTP Server Directory
2> View My Directory
3> Sets the type of file to be transferred. Current state : Ascii
4> Sets Data Connection. Current state : Active
5> Put File to Server
6> Get File from Server
----------------------------------------

6
>get file name?tftp_test_file.txt
Rcvd Command: 200 Port command successful
Rcvd Command: 150 Opening data channel for file download from server of "/tftp_test_file.txt"
get waiting...
HelloWIZNET
Rcvd Command: 226 Successfully transferred "/tftp_test_file.txt"

测试前提

测试实例需要 PC 端和 W6300 处于同一网段。使用 FileZilla Server 作为 FTP 服务器端,操作步骤如下:第一步建立服务器并设置账户(用户名:wiznet,密码:123456);第二步添加共享文件夹,并确保要下载的文件放在共享目录下;第三步在串口助手根据提示输入用户名和密码;第四步选择 6 从服务器获取文件;第五步输入文件名,成功获取文件内容。

FileZilla Server Users 设置 — 创建用户 wiznet(第一步)
FileZilla Server Shared Folders 设置 — 添加共享目录及权限(第二步)
FileZilla Server 日志(wiznet 登录成功)+ WIZ UartTool 串口输出(FTP 菜单 + 认证 230 Logged on)— 第三步
 WIZ UartTool — 数据连接建立FileZilla Server 日志(PORT 命令)+ WIZ UartTool 串口输出(200 Port command successful)— 第四步
FileZilla Server 日志(RETR tftp_test_file.txt)+ WIZ UartTool 串口输出(HelloWIZNET + 226 Successfully transferred)— 第五步

打开 FileZilla Server,在服务器上设置好用户名、密码和共享文件夹。W6300 上电运行后,通过串口输入用户名和密码登录 FTP 服务器,然后根据菜单选择需要执行的操作。以文件下载为例,选择 6 并输入文件名后,服务器会把文件内容发送到 W6300,串口显示文件内容,最后服务器返回 226 表示传输完成。

常见问题

Q: 主动模式和被动模式如何选择

主动模式下服务器主动连接客户端的数据端口,适合服务器端网络无防火墙限制的环境。被动模式下客户端主动连接服务器指定的端口,更适合客户端在 NAT 或防火墙后的情况。W6300 例程中可通过菜单选项 4 切换 ACTIVE_MODE 和 PASSIVE_MODE。当主动模式请求失败(收到 425 响应码)时,程序会自动切换到被动模式重试。

Q: FTP 传输为何使用两个 TCP 连接

FTP 协议将控制连接(端口 21)和数据连接(端口 20 或 PASV 动态端口)分离。控制连接在整个会话期间保持,用于传输命令和响应;数据连接仅在进行文件传输或目录列表操作时建立,传输完成后即关闭。这种分离设计使得控制命令和数据传输互不干扰,提高了协议的灵活性和可靠性。W6300 例程中使用两个独立的 Socket(socket_ctrl 和 socket_data)分别处理这两个连接。

Q: W6300 实现 FTP 客户端需要哪些 Socket 资源

需要两个 Socket:一个用于控制连接(socket_ctrl),一个用于数据连接(socket_data)。在 ftpc_init() 中通过 sn_ctrl 和 sn_data 参数指定 Socket 编号。控制连接 Socket 连接到服务器的 21 端口并保持整个会话;数据连接 Socket 在需要传输数据时动态创建,主动模式下监听等待服务器连接,被动模式下主动连接服务器指定端口。

Q: 下载文件时提示 FTP operation failed 怎么办

首先确认服务器日志里是否出现了 150 Opening data channel 和 226 Successfully transferred。如果服务器已经发了数据,但客户端仍报失败,通常是因为服务器发送数据并主动 FIN 的速度很快,客户端在等数据连接进入 SOCK_ESTABLISHED 之前,Socket 已经变成了 SOCK_CLOSE_WAIT。需要在等待连接建立的逻辑里,允许 SOCK_CLOSE_WAIT 且 RX 缓冲区非空时继续读取数据。可用 Wireshark 抓包确认服务器是否发了数据、客户端是否 ACK,以定位问题。

总结

  • 协议架构:FTP 采用客户端-服务器架构,使用控制连接(端口 21)和数据连接双 TCP 连接,支持主动和被动两种传输模式
  • 核心函数:ftpc_init() 初始化客户端配置,ftpc_run() 驱动控制与数据两个 Socket,ftpc_do_get() / ftpc_do_put() / ftpc_do_list() 分别处理下载、上传和目录列表
  • 文件操作:支持目录查看(LIST)、文件上传(STOR)、文件下载(RETR)等 FTP 标准命令
  • 模式切换:主动模式失败时自动切换被动模式,适应不同网络环境(NAT/防火墙)
  • 应用价值:适用于固件升级、数据采集上传、远程配置与日志管理等嵌入式场景

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5

Andy

W5500 技术支持专家