Erlang/OTP FTP 客户端实战指南:从连接、登录到文件传输的完整会话解析
编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载导读本文以 Erlang/OTP 官方文档 FTP 客户端示例 为骨架结合 FTP 客户端导论 与 ftp.erl 源码级 API 文档系统讲解如何使用 OTP 内置的ftp应用完成一次完整的 FTP 会话启动应用、连接远端主机、登录认证、切换远端与本地工作目录以及下载上传文件。读完本文你将掌握ftp模块全部核心函数连接类、信息类、更新类、文件传输类、分块传输类的调用方式与配置选项能够独立编写可运行、可排错的 Erlang FTP 客户端程序。一、使用 FTP 客户端前必须知道的三件事FTP 客户端在 OTP 中被设计为临时性组件这与kernel、stdlib等常驻应用有本质区别。官方 FTP 客户端导论 明确了三点关键语义运行时启动与停止FTP 客户端只能通过ftp:start/0在运行时启动不能在应用启动阶段application startup自动拉起。进程归属与生命周期FTP 客户端 API 被设计为允许某些函数返回中间结果intermediate results这隐含一个约束——只有启动该 FTP 客户端的进程才能以保持语义一致的方式访问它。更严格的是客户端进程会监视monitor创建它的进程如果创建 FTP 会话的进程死亡FTP 客户端进程也会随之终止。这一机制在 ftp.erl 的open/2文档中再次被强调“返回的Pid在所有其他函数中作为新创建的 FTP 客户端的引用且这些函数必须由创建连接的进程调用。FTP 客户端进程监视创建它的进程并在该进程终止时终止。”IPv6 支持只要底层机制操作系统与网络栈支持FTP 客户端即可使用 IPv6无需额外配置开关。从 ftp.app.src 可以看出ftp应用对外暴露 6 个模块ftp公共 API 入口、ftp_app应用回调、ftp_progress进度回调、ftp_internal内部实现所有公共 API 均委托给它、ftp_response错误码与响应格式化、ftp_sup监督树。其中ftp.erl中的每个公共函数都只是薄薄一层转发真正的逻辑在ftp_internal.erl中例如pwd(Pid) - ftp_internal:pwd(Pid)见 ftp.erl。二、完整示例会话一段可复现的 FTP 交互官方 FTP 客户端示例 给出了一段完整的 REPL 会话——用户guest使用密码password登录远端主机erlang.org全程共 10 步1 ftp:start(). ok 2 {ok, Pid} ftp:open([{host, erlang.org}]). {ok,0.22.0} 3 ftp:user(Pid, guest, password). ok 4 ftp:pwd(Pid). {ok, /home/guest} 5 ftp:cd(Pid, appl/examples). ok 6 ftp:lpwd(Pid). {ok, /home/fred}. 7 ftp:lcd(Pid, /home/eproj/examples). ok 8 ftp:recv(Pid, appl.erl). ok 9 ftp:close(Pid). ok 10 ftp:stop(). ok逐行解读这段会话的语义第 1 步ftp:start()启动ftp应用返回ok。这是所有后续调用的前提。第 2 步ftp:open([{host, erlang.org}])以选项列表形式打开会话——启动一个 FTP 客户端进程并连接远端主机。{ok, 0.22.0}中的0.22.0就是客户端进程的 Pid后续所有函数都要以它为第一个参数。第 3 步ftp:user(Pid, guest, password)登录认证。第 4 步ftp:pwd(Pid)查询远端当前工作目录得到/home/guest。也就是说会话刚打开时远端当前目录是/home/guest。第 5 步ftp:cd(Pid, appl/examples)把远端工作目录切换到/home/guest/appl/examples。第 6 步ftp:lpwd(Pid)查询本地当前工作目录得到/home/fred。第 7 步ftp:lcd(Pid, /home/eproj/examples)把本地工作目录切换到/home/eproj/examples。第 8 步ftp:recv(Pid, appl.erl)将文件appl.erl从远端传输到本地主机。由于此前已经通过cd/lcd调整好两侧目录此时远端取的是/home/guest/appl/examples/appl.erl落盘到/home/eproj/examples/appl.erl。第 9 步ftp:close(Pid)关闭本次 FTP 会话。第 10 步ftp:stop()停止ftp应用。官方文档特别点明会话打开时远端当前目录为/home/guest、本地当前目录为/home/fred在传输文件之前本地目录被改为/home/eproj/examples远端目录被设为/home/guest/appl/examples。这组目录切换动作正是cd远端与lcd本地的典型分工。本地工作目录的确定规则值得注意open/2的文档ftp.erl指出会话打开后本地当前工作目录取自file:get_cwd/1的返回值。因此上例中lpwd得到/home/fred实际取决于启动 Erlang shell 时的进程工作目录而非写死的值。三、连接类 API会话的开、合与认证ftp模块按功能将 API 划分为 Connection、Info、Update、File Transfer、Chunk File Transfer 几个组见 ftp.erl 中各-doc(#{group ...})标注。本节先讲连接类。3.1ftp:start/0与ftp:stop/0start() - application:start(ftp). % ftp.erl L123-L124 stop() - application:stop(ftp). % ftp.erl L127-L128二者只是application:start/1、application:stop/1的薄封装。依据 ftp.app.srcftp应用依赖kernel与stdlib运行时还依赖ssl用于 FTPS 支持见runtime_dependencies中的ssl-10.2。3.2ftp:open/1与ftp:open/2打开会话有四种等价写法%% 形式一仅主机等价于 open/2见 ftp.erl L139-L150 {ok, Pid} ftp:open(erlang.org). %% 形式二主机 端口ftp.erl L311-L312转发 ftp_internal:open/2 {ok, Pid} ftp:open(erlang.org, 21). %% 形式三选项列表官方示例所用ftp.erl L145-L150 {ok, Pid} ftp:open([{host, erlang.org}]). %% 形式四向后兼容的 {option_list, Opts}ftp.erl L145-L147 {ok, Pid} ftp:open({option_list, [{host, erlang.org}]}).返回值统一为{ok, ClientPid} | {error, Reason}其中client()类型即pid()ftp.erl。open/2的完整选项签名ftp.erl为Opt :: {verbose, Verbose} | {debug, Debug} | {ipfamily, IpFamily} | {port, Port} | {mode, Mode} | {tls, TLSOptions} | {tls_sec_method, TLSSecMethod} | {tls_ctrl_session_reuse, TLSSessionReuse} | {timeout, Timeout} | {dtimeout, DTimeout} | {progress, Progress} | {sock_ctrl, SocketCtrls} | {sock_data_act, [SocketControl]} | {sock_data_pass, [SocketControl]}3.3 登录与账户ftp:user/3、ftp:user/4、ftp:account/2%% 用户名 密码ftp.erl L321-L327 ok ftp:user(Pid, guest, password). %% 用户名 密码 账户名ftp.erl L333-L340用于需要 ACCT 命令的服务器 ok ftp:user(Pid, guest, password, my_account). %% 为某次操作单独设置账户ftp.erl L350-L354 ok ftp:account(Pid, my_account).3.4 关闭会话ftp:close/1ok ftp:close(Pid). % 结束 open/2 创建的会话见 ftp.erl L844-L846四、目录与信息查询 API远端与本地工作目录FTP 会话天然存在“远端目录”与“本地目录”两条平行路径ftp模块对此的命名约定是不带l前缀的操作作用于远端服务器带llocal前缀的操作作用于本地客户端。函数作用对象说明pwd(Pid)远端返回远端当前工作目录如{ok, /home/guest}ftp.erllpwd(Pid)本地返回本地当前工作目录如{ok, /home/fred}ftp.erlcd(Pid, Dir)远端切换远端工作目录ftp.erllcd(Pid, Dir)本地切换本地工作目录ftp.erl{ok, /home/guest} ftp:pwd(Pid), ok ftp:cd(Pid, appl/examples), {ok, /home/fred} ftp:lpwd(Pid), ok ftp:lcd(Pid, /home/eproj/examples).目录列表ls与nlistls(Pid)/ls(Pid, Dir)返回长格式long format文件列表。Dir可以是目录或文件且可包含通配符。返回格式依赖操作系统——在 UNIX 上通常来自ls -l的输出ftp.erl。ls/1隐含作用于用户当前的远端目录。nlist(Pid)/nlist(Pid, Pathname)返回短格式文件列表——一串文件名每条以CRLF或NL分隔。与ls不同nlist的目的就是让程序能够自动处理文件名信息ftp.erl。{ok, Listing} ftp:ls(Pid), %% 长格式人工可读 {ok, Names} ftp:nlist(Pid, *.erl) %% 短格式机器可解析五、文件传输 API下载、上传与追加5.1 下载recv/2、recv/3、recv_bin/2recv把远端文件传输到本地文件系统%% 两参形式本地文件名与远端同名ftp.erl L549-L553 ok ftp:recv(Pid, appl.erl). %% 三参形式显式指定本地文件名ftp.erl L555-L570 ok ftp:recv(Pid, appl.erl, /home/eproj/examples/appl.erl).官方文档明确了两点边界行为ftp.erl若指定LocalFileName本地文件即以此为名否则与RemoteFileName同名若本地文件写入失败命令中止并返回{error, term()}但已创建的文件不会被删除。recv_bin则把远端文件直接以二进制形式取回适合内存处理{ok, Bin} ftp:recv_bin(Pid, appl.erl). % 见 ftp.erl L578-L5875.2 上传send/2、send/3、send_bin/3send把本地文件发送到远端%% 两参形式远端文件名与本地同名ftp.erl L630-L636 ok ftp:send(Pid, appl.erl). %% 三参形式显式指定远端文件名ftp.erl L638-L650 ok ftp:send(Pid, appl.erl, appl_remote.erl). %% 直接把二进制内容写入远端文件ftp.erl L656-L664 ok ftp:send_bin(Pid, file content, note.txt).5.3 追加append/2、append/3、append_bin/3append系列把本地文件/二进制追加到远端文件末尾若远端文件不存在则创建ftp.erlok ftp:append(Pid, log.txt), % 追加到同名远端文件 ok ftp:append(Pid, log.txt, server_log.txt), % 追加到指定远端文件 ok ftp:append_bin(Pid, more data, log.txt). % 追加二进制5.4 传输类型type/2会话刚打开时使用 FTP 服务器的默认传输类型通常是 ASCIIRFC 959 的默认值。可用type/2显式切换为ascii或binaryftp.erlok ftp:type(Pid, binary). % 二进制传输适合非文本文件 ok ftp:type(Pid, ascii). % ASCII 传输六、分块传输 API流式处理大文件当文件过大、希望边传输边处理如流式写入数据库、实时转发时可用分块chunkAPI。它的特点是把一次传输拆成 start / 多次 chunk / end 三个阶段%% 上传分块先 start再多次 send_chunk最后 send_chunk_end ok ftp:send_chunk_start(Pid, big_remote.bin), ok ftp:send_chunk(Pid, Chunk1/binary), ok ftp:send_chunk(Pid, Chunk2/binary), ok ftp:send_chunk_end(Pid). % 服务器关闭文件ftp.erl L734-L744 %% 下载分块recv_chunk 反复调用直到返回 ok ok ftp:recv_chunk_start(Pid, big_remote.bin), case ftp:recv_chunk(Pid) of ok - done; {ok, Bin} - handle(Bin), again; % 还有更多数据 {error, Reason} - failed end. %% 追加分块文件不存在则创建 ok ftp:append_chunk_start(Pid, big_remote.bin), ok ftp:append_chunk(Pid, Chunk/binary), ok ftp:append_chunk_end(Pid).recv_chunk/1的三种返回值语义ftp.erlok—— 传输完成{ok, Bin}—— 又收到一块文件数据{error, Reason}—— 传输失败。一个重要的排错细节对于某些错误例如文件系统已满需要调用send_chunk_end或append_chunk_end才能拿到真正的失败原因ftp.erl 与 ftp.erl。七、open/2 配置选项全解open/2的选项直接影响连接行为、超时控制、TLS 加密与底层套接字。以下参数全部出自 ftp.erl 的官方文档。7.1 连接基础host / port / mode / ipfamily选项默认值说明{host, Host}无必填之一Host string() \| ip_address(){port, Port}00表示别名到21若配合{tls_sec_method, ftps}则别名到990{mode, Mode}passiveactive \| passive即 FTP 主动/被动数据连接模式{ipfamily, IpFamily}inetIPv4inet \| inet6 \| inet6fb4。inet6fb4保持旧行为优先 IPv6失败后才回退 IPv47.2 超时控制timeout / dtimeout选项默认值说明{timeout, Timeout}60000毫秒连接超时{dtimeout, DTimeout}infinity数据连接超时——客户端等待服务器连入数据套接字的时间7.3 日志与调试verbose / debug{verbose, true} % 是否打印 FTP 通信过程默认 false {debug, trace} % 使用 dbg 工具调试取值 disable | debug | trace默认 disable7.4 FTPS 加密tls / tls_sec_method / tls_ctrl_session_reuse%% 显式 FTPSSTARTTLS 升级tls_sec_method 默认 ftpes {ok, Pid} ftp:open([{host, ftps.example.com}, {tls, []}, {tls_sec_method, ftpes}]). %% 隐式 FTPS连接即 SSL端口自动别名到 990 {ok, Pid} ftp:open([{host, ftps.example.com}, {tls, []}, {tls_sec_method, ftps}]).{tls, TLSOptions}将整个 FTP 会话承载于 TLS 之上ftps对应 [RFC 4217]TLSOptions列表可以为空。底层通过ssl:connect/3同时加固控制连接与数据连接。{tls_sec_method, ftps | ftpes}ftps表示连接后立即使用 SSL而非通过 STARTTLS 升级该子选项只有在同时设置了tls时才生效。默认ftpes。{tls_ctrl_session_reuse, boolean()}设为true时客户端在数据通道上复用控制通道的 TLS 会话——这是许多 FTP 服务器强制要求的做法如 vsftpd 率先提出并实现。默认false。7.5 底层套接字透传sock_ctrl / sock_data_act / sock_data_pass这三个选项把控制权下放给gen_tcp层{sock_ctrl, [{nodelay, true}]} % 控制连接的 TCP 选项默认 [] {sock_data_act, [{nodelay, true}]} % 主动模式数据连接默认取 sock_ctrl 的值 {sock_data_pass, [{nodelay, true}]} % 被动模式数据连接默认取 sock_ctrl 的值SocketControl是gen_tcp:option()但排除ipv6_v6only、active、packet、mode、packet_size与header这些由 FTP 客户端内部管理。7.6 进度回调progressprogress用于实现 GUI 进度条之类的进度报告取值ignore | {Module, Function, InitialData}默认ignore。当设置后每次调用ftp:send/[3,4]或ftp:recv/[3,4]时触发以下回调序列ftp.erl%% 1) 传输开始前告知文件大小 Module:Function(InitialData, File, {file_size, FileSize}) %% 2) 每传输一块字节 Module:Function(UserProgressTerm, File, {transfer_size, TransferSize}) %% 3) 文件传输结束时 Module:Function(UserProgressTerm, File, {transfer_size, 0})回调函数约定签名Module:Function(UserProgressTerm, File, Size) - UserProgressTerm其中Size {transfer_size, integer()} | {file_size, integer()} | {file_size, unknown}。对于远端文件ftp无法以平台无关的方式确定文件大小此时file_size为unknown由应用自行处理。回调返回值作为下一次调用的UserProgressTerm输入。实现细节回调由中间人middleman进程执行因此回调代码不会影响文件传输本身若回调崩溃FTP 连接进程会检测到并打印一条 info-report随后像progress被设为ignore一样继续工作ftp.erl。八、错误处理、诊断与扩展命令8.1 可读化错误formaterror/1formaterror把{error, AtomReason}中的原子错误转成可读字符串ftp.erl底层委托给ftp_response:error_string/1。它覆盖从连接ehost、econnrefused到协议euser、elogin、epath、efnamena等的一系列错误码。8.2 最近响应latest_ctrl_response/1Latest ftp:latest_ctrl_response(Pid). % 返回服务器最近一次响应的原始文本8.3 扩展命令quote/2quote发送任意 FTP 命令并原样返回服务器回复的行列表ftp.erl用于访问服务器特有或本客户端未提供的命令。两个注意点FTP 协议定义的行尾 CRLF\r\n已被剥离无需自行携带需要数据连接的命令如LIST、RETR无法通过quote成功执行。Lines ftp:quote(Pid, SITE CHMOD 755 appl.erl). % 服务器特有命令示例8.4 错误返回的常规形态绝大多数 API 返回ok | {error, Reason}部分如pwd、recv_bin返回{ok, Value} | {error, Reason}。官方测试 ftp_SUITE.erl 提供了大量可复现的断言例如非法用户登录返回{error, euser}L458、不存在的文件发送返回{error, epath}L590、不存在的远端下载返回{error, epath}L747、未登录即发送返回{error, elogin}L1062、无法解析主机返回{error, ehost}L1072。这些用例同时演示了ftp:open(127.0.0.1, [{port, FtpPort}])与ftp:open([{host, Host}, {port, Port} | Options])两种打开方式L1145、L1315-L1326。九、源码结构速览调用链与模块分工从 ftp.app.src 的modules列表可清晰看到分层设计ftp—— 公共 API 门面全部-export见 ftp.erl含-doc文档与-spec类型签名ftp_internal—— 实现层ftp模块的每个函数几乎都一行转发到此如cd/2转发ftp_internal:cd/2见 ftp.erlftp_response—— 错误码与错误字符串ftp_progress—— progress 回调的中间人进程实现ftp_sup/ftp_app—— 监督树与应用回调。此外ftp.erl顶部的-deprecated标注ftp.erl值得留意FTP 被视为遗留协议OTP 官方计划在OTP-30 中移除遗留协议支持并建议改用更现代的文件传输方案例如SFTPSSH File Transfer Protocol。在新建项目时这一点应纳入技术选型考量而基于本仓库编写 FTP 客户端代码时仍可放心使用上述全部 API。结语一次典型的 OTP FTP 客户端会话遵循“start→open→user→ 目录操作/文件传输 →close→stop”的固定节奏其中cd/lcd分别治理远端与本地两条目录路径recv/send及分块变体覆盖了从单文件到流式大文件的全部传输场景而open/2的十余个选项则把超时、被动/主动模式、IPv6、FTPS 加密与进度回调全部收敛到一次调用之中。结合本文列出的源码路径ftp_client.md、introduction.md、ftp.erl、ftp_internal.erl、ftp.app.src、ftp_SUITE.erl你可以继续深入追踪每个函数的底层实现与测试覆盖快速上手自己的 FTP 集成需求。赞分享编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载相关推荐goftp 客户端库实战scan4all 如何用 jlaffaye/ftp 完成 FTP 连接、登录与文件传输goftp 客户端库实战scan4all 如何用 jlaffaye/ftp 完成 FTP 连接、登录与文件传输 本篇以 vendored 的 goftp 库网络安全漏洞扫描渗透测试应用安全Node.js FTP客户端完整指南从入门到实战Node.js FTP客户端完整指南从入门到实战 还在为Node.js项目中的文件传输需求而烦恼吗传统的文件传输方式往往需要复杂的配置和繁琐的操作流程。js网络与通信终极轻量级FTP客户端FFFTP的完整文件传输解决方案终极轻量级FTP客户端FFFTP的完整文件传输解决方案 还在为复杂的文件传输工具而烦恼吗 今天为大家推荐一款简单实用的FTP客户端——FFFTP。这款轻上一篇TCRT5_pre_tcrdb vs 传统方法为什么预训练模型是免疫序列设计的未来下一篇Mason.nvimNeovim的便携式包管理器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考