W6300作为FTP客户端
更新于 2026年10月19日
本篇总结
W6300 实现 FTP 客户端功能,可访问 FTP 服务器进行文件上传、下载和目录管理。
硬件平台:本例程基于 W6300-EVB-M4 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板
本示例演示如何在 W6300 上实现 FTP(文件传输协议)的客户端模式,通过实战例程讲解 FTP 文件上传、下载和目录管理操作。该例程用到的其他网络协议,例如 DHCP,请参考相关章节。
前置工具准备
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 客户端连接服务器进行文件操作。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
- 确保 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 │
└───────────┘
工程结构
工程核心文件如下:
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,在服务器上设置好用户名、密码和共享文件夹。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/防火墙)
- 应用价值:适用于固件升级、数据采集上传、远程配置与日志管理等嵌入式场景