05 FTP
一、什么是 FTP
1.1 基本概念
FTP 协议
在 4G 物联网场景中,FTP 协议(File Transfer Protocol,文件传输协议)是一种基于 TCP/IP 的标准化文件传输技术。
FTP 协议包括两个组成部分,其一为 FTP 服务器,其二为 FTP 客户端.
FTP 服务器
FTP 服务器(File Transfer Protocol Server)是基于 FTP 协议提供文件存储、传输与管理服务的系统。简单来说,支持 FTP 协议的服务器就是 FTP 服务器, 用来存储文件,用户可以使用 FTP 客户端通过 FTP 协议访问位于 FTP 服务器上的资源.
FTP 客户端
FTP 客户端(FTP Client)是指用户端用于连接 FTP 服务器、并进行文件传输及管理操作的工具或软件。它是 FTP 通信中的 “发起方”,负责向 FTP 服务器发送连接请求、认证信息(如用户名和密码),并执行上传、下载、删除、移动文件等操作。
默认情况下 FTP 协议使用 TCP 端口中的 20 和 21 这两个端口,其中 20 用于传输数据,21 用于传输控制信息.但是,是否使用 20 作为传输数据的端口与 FTP 使用的传输模式有关:
如果采用主动模式,那么数据传输端口就是 20;
如果采用被动模式,要服务器端和客户端协商决定最终使用哪个端口.
FTP 作为成熟的文件传输协议,在 4G 物联网场景中承担 “标准化文件交互通道” 的角色,弥补了物联网中轻量级协议(如 MQTT)在大文件传输上的不足.
1.2 关键特性
- 基于 TCP 的可靠性:FTP 依赖 TCP 协议实现连接,通过三次握手建立连接、重传机制保障数据完整性,适合物联网中 “固件包”“监控录像” 等关键文件的传输,避免丢失或损坏。
- 双向文件操作:支持 “上传”(设备 → 服务器,如传感器日志上传)和 “下载”(服务器 → 设备,如固件更新),满足物联网设备与云端的双向数据交互需求。
- 适配 4G 网络特性:4G 的高带宽支持 FTP 传输较大文件;低延迟特性减少了 FTP 控制连接(端口 21)和数据连接(端口 20)的交互等待时间,提升传输效率。
1.3 典型应用场景
- 设备固件升级:物联网设备(如工业网关、智能摄像头)通过 4G 网络连接 FTP 服务器,下载最新固件包并完成升级,无需现场操作。
- 批量数据回传:部署在户外的传感器(如环境监测设备)定期将采集的日志文件(如温度、湿度历史数据)通过 FTP 上传至云端服务器,便于集中分析。
- 多媒体文件传输:车载终端、安防摄像头等设备通过 4G 网络将拍摄的视频、图片文件经 FTP 传输至后端平台,实现远程监控。
综上,4G 物联网中的 FTP 是一种依托 4G 高带宽、低延迟特性,实现设备与云端大文件可靠传输的成熟方案,广泛应用于固件升级、数据回传、多媒体传输等场景。
关于 FTP 更详细的理论知识说明,请参考:https://blog.csdn.net/HinsCoder/article/details/130911658
或者其他途径自行参考。
下面就跟着教程用合宙模组实际操作演示一下 FTP 协议的实际应用。
二、功能演示概述
本文演示合宙 4G 模组使用 LuatOS 开发时, 4G 通信中 ftp 网络协议的应用功能.
使用 Air8000 核心板下载 Air8000 的 LuatOS 示例代码中 ftp 的例程进行验证,例程中实现的功能核心业务逻辑为:
- 配置 FTP 客户端登录服务器的参数和文件路径
- 封装一个重试机制,在登录失败、上传文件失败或者下载文件失败时尝试重新执行操作
- 登录 FTP 服务器,通过重试机制确保登录成功
- ftp.push 上传本地文件到服务器,在本地新建文件并写入内容后上传到服务器指定路径,通过重试机制确保上传成功
- ftp.pull 从服务器下载文件,保存在本地指定路径,并读取文件长度,当长度小于指定字节时,读取文件内容,通常是设定 512 字节,如果文件太大,会消耗 ram,通过重试机制确保上传成功
- 主函数循环运行以下流程: 登录服务器、用 ftp.command 操作 ftp 服务器目录以及文件上传下载处理后关闭服务器。
三、准备硬件环境
参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。
四、准备软件环境
在开始实践本示例之前,先准备一下软件环境:
4.1 Luatools 工具,如果是第一次使用 Luatools 工具,请仔细阅读此链接教程。
4.2 内核固件文件(底层 core 固件文件):LuatOS-SoC_V2014_Air8000_1.soc;参考项目使用的内核固件;如有更新可以使用最新固件。
4.3 luatos 需要的脚本和资源文件:https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/ftp
main.lua:主程序入口;
netdrv_device.lua:网卡驱动设备,可以配置使用netdrv文件夹内的四种网卡(单4g网卡,单wifi网卡,单spi以太网卡,多网卡)中的任何一种网卡;
netdrv 文件夹:四种网卡,单 4g 网卡、单 wifi 网卡,、单 spi 以太网卡、多网卡,供 netdrv_device.lua 加载配置;
ftp_up_download.lua,功能演示核心脚本,在 main.lua 中加载运行。
4.4 lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到 Air8000 核心板,将本篇文章中演示使用的项目文件烧录到 Air8000 核心板中。
4.5 访问 ftp 服务器
以下准备了三种访问 ftp 服务器的方法
本文使用方法 1,通过 Windows 资源管理器访问,若无法访问,请尝试方法 2 或者方法 3:
测试服务器客户端配置参数,非 ssl 加密: local server_ip = "121.43.224.154" -- 服务器 IP
local server_port = 21 -- 服务器端口号
local server_username = "ftp_user" -- 服务器登陆用户名
local server_password = "3QujbiMG" -- 服务器登陆密码
方法 1 Windows 资源管理器: - 打开“我的电脑”,在地址栏输入“ftp://121.43.224.154/”,按回车键。
在登录界面输入用户名和密码,点击登录,即可看到服务器上保存的文件。
方法 2 IE 浏览器访问: - 打开 IE 浏览器,在浏览器输入 121.43.224.154,回车,然后输入用户名和密码,点击登录,进入服务器。
方法 3 客户端软件:下载安装 Xftp 客户端软件,并配置好 ftp 服务器地址,端口号,用户名,密码等信息,即可登录,安装教程可自行上网查找。
注意选择 FTP 协议,如果新建会话后无法打开会话,可以再次检查登录信息是否正确。
五、API 接口说明
六、示例代码和功能展示
6.1 流程介绍
-
搭建好硬件环境
-
demo 脚本代码 netdrv_device.lua 中,按照自己的网卡需求启用对应的 Lua 文件
-
如果需要单 4G 网卡,打开 require "netdrv_4g",其余注释掉
-
如果需要单 WIFI STA 网卡,打开 require "netdrv_wifi",其余注释掉;同时 netdrv_wifi.lua 中的 wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1),前两个参数,修改为自己测试时 wifi 热点的名称和密码;注意:仅支持 2.4G 的 wifi,不支持 5G 的 wifi
-
如果需要以太网卡,打开 require "netdrv_eth_spi",其余注释掉
-
如果需要多网卡,打开 require "netdrv_multiple",其余注释掉;同时 netdrv_multiple.lua 中的 ssid = "茶室-降功耗,找合宙!", password = "Air123456", 修改为自己测试时 wifi 热点的名称和密码;注意:仅支持 2.4G 的 wifi,不支持 5G 的 wifi
-
本示例使用的服务器参考第四章节 5. 访问 ftp 服务器,如果是自己的 ftp 服务器,在脚本代码 ftp_up_download.lua 的 config 表中按自己的服务器 IP,端口号,用户名,密码修改参数。
-
Luatools 烧录内核固件和修改后的 demo 脚本代码
-
烧录成功后,自动开机运行,查看打印日志,如果正常运行,会打印 ftp 登录成功,文件目录,上传下载文件结果等信息。
6.2 代码和 log
6.2.1 代码示例(点击查看 ftp 的完整 demo)
PROJECT = "ftp_demo"
VERSION = "001.000.000"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
if wdt then
--配置喂狗超时时间为9秒钟
wdt.init(9000)
--启动一个循环定时器,每隔3秒钟喂一次狗
sys.timerLoopStart(wdt.feed, 3000)
end
-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
-- 启动errDump日志存储并且上传功能,600秒上传一次
-- if errDump then
-- errDump.config(true, 600)
-- end
-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
-- 可以使用合宙的iot.openluat.com平台进行远程升级
-- 也可以使用客户自己搭建的平台进行远程升级
-- 远程升级的详细用法,可以参考fota的demo进行使用
-- 启动一个循环定时器
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
-- 方便分析内存使用是否有异常
-- sys.timerLoopStart(function()
-- log.info("mem.lua", rtos.meminfo())
-- log.info("mem.sys", rtos.meminfo("sys"))
-- end, 3000)
-- 加载网络驱动设备功能模块,在该文件中修改自己使用的联网方式
require"netdrv_device"
--加载ftp_up_download功能模块
require "ftp_up_download"
-- 启动系统调度(必须放在最后)
sys.run()
6.2.2 核心代码部分(点击查看 ftp 的完整 demo)
--1. 统一配置FTP参数和文件路径
local config = {
server = {
ip = "121.43.224.154", -- FTP服务器IP
port = 21, -- FTP端口
username = "ftp_user", -- 登陆用户名
password = "3QujbiMG", -- 登陆密码
is_ssl = false, -- 若需SSL,补充证书
},
download = {
remote_file = "/12222.txt", -- 服务器上要下载的文件
local_file = "/ftp_download.txt" -- 本地保存的文件名
},
upload = {
local_file = "/ftp_upload.txt", -- 本地要上传的文件(会自动创建)
remote_file = "/uploaded_by_luatos.txt" -- 服务器保存的文件名
}
}
--2.定义功能函数: 重试机制封装,针对易失败操作
local function retry_operation(operation, max_retries, interval)
local retries = 0
while retries < max_retries do
local result = operation()
if result then return true end
retries = retries + 1
log.warn("操作失败,重试第", retries, "次")
sys.wait(interval)
end
log.error("超过最大重试次数")
return false
end
--3.定义功能函数: 登录FTP服务器(带重试)
local function ftp_login()
--查看网卡适配器的联网状态是否IP_READY,true表示已经准备好可以联网了,false暂时不可以联网
while not socket.adapter(socket.dft()) do
log.warn("ftp_login", "wait IP_READY", socket.dft())
-- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
-- 或者等待1秒超时退出阻塞等待状态;
-- 注意:此处的1000毫秒超时不要修改的更长;
-- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
-- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
-- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
sys.waitUntil("IP_READY", 1000)
end
-- 检测到了IP_READY消息,设置默认网络适配器编号
log.info("ftp_login", "recv IP_READY", socket.dft())
--登录FTP服务器核心函数
local function ftp_login_result()
local login_result = ftp.login(
nil,
config.server.ip,
config.server.port,
config.server.username,
config.server.password,
config.server.is_ssl
).wait()
if login_result then
log.info("FTP登录成功")
return true
end
log.error("FTP登录失败")
return false
end
-- 如果登录失败,最多重试登录3次,间隔3秒,可按需修改
return retry_operation(ftp_login_result, 3, 3000)
end
--4.定义功能函数: 上传文件到服务器(带重试)
local function ftp_upload_file()
-- 确保本地文件创建成功
-- 要写入文件的内容,按需求修改
local upload_content = "Luatos FTP上传测试数据 "
local file, err = io.open(config.upload.local_file, "w")
if not file then
log.error("创建本地文件失败:", err)
return false
end
--写入内容到文件
file:write(upload_content)
file:close()
--打印创建文件的结果
log.info("本地文件" .. config.upload.local_file .. "创建成功," .. "并写入文件内容:", upload_content)
-- 上传文件核心函数
local function ftp_upload_result()
log.info("开始上传文件:" .. config.upload.local_file)
-- ftp.push参数:本地文件路径 → 服务器文件路径
local upload_ok = ftp.push(
config.upload.local_file,
config.upload.remote_file
).wait()
if upload_ok then
log.info("本地文件上传成功,保存在服务器路径:", config.upload.remote_file)
return true
end
log.error("文件上传失败")
return false
end
-- 如果上传失败,最多重试上传3次,间隔2秒,可按需修改
return retry_operation(ftp_upload_result, 3, 2000)
end
--5.定义功能函数: 下载文件到本地(带重试)
local function ftp_download_file()
local function ftp_download_result()
log.info(" 开始下载文件:" .. config.download.remote_file)
-- ftp.pull参数: 本地文件路径→服务器文件路径
local download_ok = ftp.pull(
config.download.local_file,
config.download.remote_file
).wait()
if not download_ok then
log.error("文件下载失败")
return false
end
--检查下载结果
local file = io.open(config.download.local_file, "r")
if not file then
log.error("下载文件本地打开失败!重新下载")
return false
end
local fsize = io.fileSize(config.download.local_file)
if not fsize then
log.error("读取文件大小失败,重新下载")
return false
end
log.info("服务器上文件" .. config.download.remote_file .. "下载成功,保存在本地路径:", config.download.local_file, "大小:", fsize,
"字节")
if fsize and fsize <= 512 then
local content = file:read("*a")
file:close()
log.info("下载文件内容长度小于512字节,内容是:", content)
return true
end
-- 执行完操作后,一定要关掉文件
file:close()
end
-- 如果下载失败,最多重试10次,间隔2秒,可按需修改
return retry_operation(ftp_download_result, 10, 2000)
end
--6.定义功能函数: 功能测试主函数
local function ftp_main_task()
while true do
-- 步骤1:登录FTP服务器
if not ftp_login() then
log.error("登录失败,退出流程")
--退出前关闭连接,释放资源
ftp.close().wait()
return
end
-- 执行FTP命令并检查结果
log.info("空操作,防止连接断掉", ftp.command("NOOP").wait())
log.info("报告远程系统的操作系统类型", ftp.command("SYST").wait())
log.info("指定文件类型", ftp.command("TYPE I").wait())
log.info("显示当前工作目录名", ftp.command("PWD").wait())
log.info("创建一个目录 目录名为QWER", ftp.command("MKD QWER").wait())
log.info("改变当前工作目录为QWER", ftp.command("CWD /QWER").wait())
log.info("返回上一层目录", ftp.command("CDUP").wait())
log.info("获取当前工作目录下的文件名列表", ftp.command("LIST").wait())
-- 步骤2:上传文件
if not ftp_upload_file() then
log.error("上传失败,继续执行下载") -- 不中断流程
end
-- 步骤3:下载文件
if not ftp_download_file() then
log.error("下载失败")
end
-- 步骤4:清理操作(可选),清理之前创建的目录
log.info("删除测试目录QWER", ftp.command("RMD QWER").wait())
-- 步骤5:关闭连接
local close_result = ftp.close().wait()
log.info("FTP连接关闭结果:", close_result)
--步骤6:获取内存信息
local meminfo_result = rtos.meminfo("sys")
log.info("meminfo内存信息", rtos.meminfo("sys"))
--循环执行,每1分钟执行一次
sys.wait(1 * 60 * 1000)
end
end
-- 启动主任务
sys.taskInit(ftp_main_task)
6.2.3 例程 log 打印
[2025-08-22 16:34:47.428][000000001.784] I/user.ftp_login recv IP_READY 1 3[2025-08-22 16:34:47.434][000000001.830] D/mobile TIME_SYNC 0[2025-08-22 16:34:47.506][000000002.077] I/user.FTP 登录成功[2025-08-22 16:34:47.552][000000002.128] I/user.空操作,防止连接断掉 200 NOOP ok.
[2025-08-22 16:34:47.598][000000002.178] I/user.报告远程系统的操作系统类型 215 UNIX Type: L8
[2025-08-22 16:34:47.644][000000002.229] I/user.指定文件类型 200 Switching to Binary mode.
[2025-08-22 16:34:47.706][000000002.278] I/user.显示当前工作目录名 257 "/"
[2025-08-22 16:34:47.751][000000002.329] I/user.创建一个目录 目录名为 QWER 257 "/QWER" created
[2025-08-22 16:34:47.797][000000002.379] I/user.改变当前工作目录为 QWER 250 Directory successfully changed.
[2025-08-22 16:34:47.843][000000002.427] I/user.返回上一层目录 250 Directory successfully changed.
[2025-08-22 16:34:48.062][000000002.647] I/user.获取当前工作目录下的文件名列表[2025-08-22 16:34:48.071][000000002.647]
drwx------ 2 ftp ftp 4096 Apr 29 08:43 080307100000002
drwx------ 2 ftp ftp 4096 Apr 11 01:22 080307100000003
drwx------ 2 ftp ftp 4096 Apr 11 01:22 080307100000004
drwx------ 2 ftp ftp 4096 Apr 11 01:22 080307100000005
drwx------ 2 ftp ftp 4096 Apr 11 01:23 080307100000006
drwx------ 2 ftp ftp 4096 Apr 30 06:16 080307100000022
drwx------ 2 ftp ftp 4096 Apr 30 06:16 080307100000023
drwx------ 2 ftp ftp 4096 Apr 30 06:16 080307100000024
drwx------ 2 ftp ftp 4096 Apr 30 06:16 080307100000025
drwx------ 2 ftp ftp 4096 Apr 30 06:16 080307100000026
drwx------ 2 ftp ftp 4096 Jul 25 10:40 080307100000027
drwx------ 2 ftp ftp 4096 Jul 25 10:04 080307100000028
-rw------- 1 ftp ftp 27588 Jun 03 07:04 12222.mp4
-rw------- 1 ftp ftp 62 Aug 22 08:32 12222.txt
-rw------- 1 ftp ftp 174016 Jun 02 15:43 122224.mp4
-rw------- 1 ftp ftp 27588 Jun 02 03:54 1__19700101080014.mp4
drwx------ 2 ftp ftp 4096 Apr 19 04:49 23543121001
drwx------ 2 ftp ftp 4096 Aug 04 11:46 MT_T
drwx------ 2 ftp ftp 4096 Aug 22 08:34 QWER
drwx------ 3 ftp ftp 4096 Jul 09 08:02 Test
drwx------ 2 ftp ftp 4096 Apr 19 04:45 YKZ
-rw------- 1 ftp ftp 1186492 Apr 19 06:12 data.dat
-rw------- 1 ftp ftp 189943 Jun 27 07:04 ftp_test.mp4
-rw------- 1 ftp ftp 0 Jun 02 04:00 新建 文本文档.txt
[2025-08-22 16:34:48.079][000000002.651] I/user.本地文件/ftp_upload.txt 创建成功,并写入文件内容: Luatos FTP 上传测试数据
[2025-08-22 16:34:48.086][000000002.652] I/user.开始上传文件:/ftp_upload.txt
[2025-08-22 16:34:48.452][000000003.027] I/user.本地文件上传成功,保存在服务器路径: /uploaded_by_luatos.txt
[2025-08-22 16:34:48.546][000000003.127] I/user. 开始下载文件:/12222.txt
[2025-08-22 16:34:48.871][000000003.446] I/user.服务器上文件/12222.txt 下载成功,保存在本地路径: /ftp_download.txt 大小: 62 字节
[2025-08-22 16:34:48.880][000000003.447] I/user.下载文件内容长度小于 512 字节,内容是: 23noianfdiasfhnpqw39fhawe;fuibnnpw3fheaios;fna;osfhisao;fadsfl
[2025-08-22 16:34:48.932][000000003.517] I/user.删除测试目录 QWER 250 Remove directory operation successful.
[2025-08-22 16:34:49.056][000000003.636] I/user.FTP 连接关闭结果: 221 Goodbye.
[2025-08-22 16:34:49.061][000000003.636] I/user.meminfo 内存信息 3208112 279932 285960
6.2.4 luatools 页面显示
6.2.5 ftp 服务器查看上传的文件
七、总结
本文档主要介绍 4G 通信中 FTP 通信的应用。
结合 demo 例程讲解了 FTP 基本原理,介绍了 FTP 主要 API,旨在最简单的快速上手 Air8000 的 LuatOS 的 FTP 开发.
八、常见问题
8.1 关于不支持主动模式:
模块 ftp 默认是被动模式,不支持主动模式,无法设置主动模式,公网下要想模块正常连接服务器,服务器肯定不能设置为主动模式,否则哪家模块都不能用.
8.2 ftp 服务器用 vsftpd
vsftpd 是一个 FTP 服务器软件, 是一个 UNIX 类操作系统上运行的服务器的名字,vsftpd 是 very secure FTP daemon 的缩写,安全性是它的一个最大的特点,它可以运行在诸如 Linux、BSD、Solaris、 HP-UNIX 等系统上面,是一个完全免费的、开放源代码的 ftp 服务器软件,负责搭建和提供文件存储、访问服务(相当于 “文件仓库”),通过监听网络端口(默认 21 端口)接收客户端的连接请求,并根据配置允许用户上传、下载文件。