为了从FTP服务器下载文件,需要要实现一个简单的FTP客户端。

FTP(文件传输协议) 是 TCP/IP 协议组中的应用层协议。

FTP协议使用字符串格式命令字,每条命令都是一行字符串,以“\r\n”结尾。

客户端发送格式是:命令+空格+参数+"\r\n"的格式

服务器返回格式是以:状态码+空格+提示字符串+"\r\n"的格式,代码只要解析状态码就可以了。

读写文件需要登陆服务器,特殊用户名:anonymous,表示匿名。注意大小写敏感。

从FTP服务器下载文件的基本流程如下:

1. 建立TCP连接,该协议默认使用21端口,当然可以指定其它端口,取决于服务器的配置。

2. 连接成功之后,服务器会发送一行欢迎文字,例如:220 welcome.
    其中左边的数字220表示就绪状态,220后面有一个空格,空格后面是提示文字。
    在解析命令应答的时候,只需要获取前面的数字即可。

3. 收到欢迎信息后,就要开始登陆了,先用USER命令发送用户名,服务器返回331状态。
    然后再用PASS命令发送登陆密码,服务器返回530表示密码错误,返回230表示密码正确。
    发送:USER anonymous
    接收:331 Anonymous login ok, send your complete email address as your password
    发送:PASS anonymous
    接收:230 Anonymous access granted, restrictions apply

4. 登陆成功之后,再发送一条TYPE I命令,进入二进制模式,这样获取文件数据的时候,就会以二进制字节流发送。
    避免以ASCII码格式发送文件数据。

5. 获取文件长度
   发送:SIZE /path/filename
   失败:550 /path/filename: No such file or directory
   成功:213 [filesize]
   返回[filesize]是十进制数字,表示该文件在大小,字节为单位

6. 下载文件
   下载文件前,先发送PASV命令,进入被动模式,这样FTP服务器就会开放一个新的端口,用于接收文件数据。
   客户端成功连接到这个数据端口后,再发送RETR命令请求下载文件,这时文件数据就会从新的端口发送过来,文件传输完毕,服务器自动关闭数据端口。
   发送:PASV
   接收:227 Entering Passive Mode (145,24,145,107,207,235).
   后面括号内的5个数字,分别表示IP地址和端口号,端口号分为高8位和低8位,高8位在前
   这里的例子表示IP地址为145.24.145.107,端口号为53227(计算公式:207 * 256 + 235)。

发送:RETR /path/filename
   接收:150 Opening BINARY mode data connection for /path/filename (54 bytes)
   >>>从数据端口接收文件数据
   接收:226 Transfer complete

7. 上传文件

上传文件与下载文件类似,也是发送PASV命令,获取到数据端口,然后发送STOR命令请求上传文件。
  不同的是上传文件是往这个数据端口写文件数据,写完数据后,客户端主动断开与数据端口的TCP连接,服务器会返回一条上传成功的状态。
  发送:PASV
  接收:227 Entering Passive Mode (145,24,145,107,207,235).
  发送:STOR /path/filename
  接收:150 Opening BINARY mode data connection for /path/filename
   >>>往数据端口写数据
  接收:226 Transfer complete

=======================================

FTP常见的状态码如下:

110重新启动标记答复。
120服务已就绪,在nnn分钟后开始。
125数据连接已打开,正在开始传输。
150文件状态正常,准备打开数据连接。
2xx-肯定的完成答复一项操作已经成功完成。客户端可以执行新命令。
200命令确定。
202未执行命令,站点上的命令过多。
211系统状态,或系统帮助答复。
212目录状态。
213文件状态。
214帮助消息。
215NAME系统类型,其中,NAME是AssignedNumbers文档中所列的正式系统名称。
220服务就绪,可以执行新用户的请求。
221服务关闭控制连接。如果适当,请注销。
225数据连接打开,没有进行中的传输。
226关闭数据连接。请求的文件操作已成功(例如,传输文件或放弃文件)。
227进入被动模式(h1,h2,h3,h4,p1,p2)。
230用户已登录,继续进行。
250请求的文件操作正确,已完成。
257已创建“PATHNAME”。
3xx-肯定的中间答复该命令已成功,但服务器需要更多来自客户端的信息以完成对请求的处理。
331用户名正确,需要密码。
332需要登录帐户。
350请求的文件操作正在等待进一步的信息。
4xx-瞬态否定的完成答复该命令不成功,但错误是暂时的。如果客户端重试命令,可能会执行成功。
421服务不可用,正在关闭控制连接。如果服务确定它必须关闭,将向任何命令发送这一应答。
425无法打开数据连接。 426Connectionclosed;transferaborted.
450未执行请求的文件操作。文件不可用(例如,文件繁忙)。
451请求的操作异常终止:正在处理本地错误。
452未执行请求的操作。系统存储空间不够。
5xx-永久性否定的完成答复该命令不成功,错误是永久性的。如果客户端重试命令,将再次出现同样的错误。
500语法错误,命令无法识别。这可能包括诸如命令行太长之类的错误。
501在参数中有语法错误。
502未执行命令。
503错误的命令序列。
504未执行该参数的命令。
530未登录。
532存储文件需要帐户。
550未执行请求的操作。文件不可用(例如,未找到文件,没有访问权限)。
551请求的操作异常终止:未知的页面类型。
552请求的文件操作异常终止:超出存储分配(对于当前目录或数据集)。
553未执行请求的操作。不允许的文件名。

=============================================================
用C语言实现了一个简单的FTP模块,支持上传和下载文件。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "socket.h"
#include "log.h"
#include "ftp.h"static int  m_socket_cmd;
static int  m_socket_data;
static char m_send_buffer[1024];
static char m_recv_buffer[1024];//命令端口,发送命令
static int ftp_send_command(char *cmd)
{int ret;LOG_INFO("send command: %s\r\n", cmd);ret = socket_send(m_socket_cmd, cmd, (int)strlen(cmd));if(ret < 0){LOG_INFO("failed to send command: %s",cmd);return 0;}return 1;
}//命令端口,接收应答
static int ftp_recv_respond(char *resp, int len)
{int ret;int off;len -= 1;for(off=0; off<len; off+=ret){ret = socket_recv(m_socket_cmd, &resp[off], 1);if(ret < 0){LOG_INFO("recv respond error(ret=%d)!\r\n", ret);return 0;}if(resp[off] == '\n'){break;}}resp[off+1] = 0;LOG_INFO("respond:%s", resp);return atoi(resp);
}//设置FTP服务器为被动模式,并解析数据端口
static int ftp_enter_pasv(char *ipaddr, int *port)
{int ret;char *find;int a,b,c,d;int pa,pb;ret = ftp_send_command("PASV\r\n");if(ret != 1){return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 227){return 0;}find = strrchr(m_recv_buffer, '(');sscanf(find, "(%d,%d,%d,%d,%d,%d)", &a, &b, &c, &d, &pa, &pb);sprintf(ipaddr, "%d.%d.%d.%d", a, b, c, d);*port = pa * 256 + pb;return 1;
}//上传文件
int  ftp_upload(char *name, void *buf, int len)
{int  ret;char ipaddr[32];int  port;//查询数据地址ret=ftp_enter_pasv(ipaddr, &port);if(ret != 1){return 0;}ret=socket_connect(m_socket_data, ipaddr, port);if(ret != 1){return 0;}//准备上传sprintf(m_send_buffer, "STOR %s\r\n", name);ret = ftp_send_command(m_send_buffer);if(ret != 1){return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 150){socket_close(m_socket_data);return 0;}//开始上传ret = socket_send(m_socket_data, buf, len);if(ret != len){    LOG_INFO("send data error!\r\n");socket_close(m_socket_data);return 0;}socket_close(m_socket_data);//上传完成,等待回应ret = ftp_recv_respond(m_recv_buffer, 1024);return (ret==226);
}//下载文件
int  ftp_download(char *name, void *buf, int len)
{int   i;int   ret;char  ipaddr[32];int   port;//查询数据地址ret = ftp_enter_pasv(ipaddr, &port);if(ret != 1){return 0;}//连接数据端口ret = socket_connect(m_socket_data, ipaddr, port);if(ret != 1){LOG_INFO("failed to connect data port\r\n");return 0;}//准备下载sprintf(m_send_buffer, "RETR %s\r\n", name);ret = ftp_send_command(m_send_buffer);if(ret != 1){return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 150){socket_close(m_socket_data);return 0;}//开始下载,读取完数据后,服务器会自动关闭连接for(i=0; i<len; i+=ret){ret = socket_recv(m_socket_data, ((char *)buf) + i, len);LOG_INFO("download %d/%d.\r\n", i + ret, len);if(ret < 0){LOG_INFO("download was interrupted.\r\n");break;}}//下载完成LOG_INFO("download %d/%d bytes complete.\r\n", i, len);socket_close(m_socket_data);ret = ftp_recv_respond(m_recv_buffer, 1024);return (ret==226);
}//返回文件大小
int  ftp_filesize(char *name)
{int ret;int size;sprintf(m_send_buffer,"SIZE %s\r\n",name);ret = ftp_send_command(m_send_buffer);if(ret != 1){return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 213){return 0;}size = atoi(m_recv_buffer + 4);return size;
}//登陆服务器
int ftp_login(char *addr, int port, char *username, char *password)
{int ret;LOG_INFO("connect...\r\n");ret = socket_connect(m_socket_cmd, addr, port);if(ret != 1){LOG_INFO("connect server failed!\r\n");return 0;}LOG_INFO("connect ok.\r\n");//等待欢迎信息ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 220){LOG_INFO("bad server, ret=%d!\r\n", ret);socket_close(m_socket_cmd);return 0;}LOG_INFO("login...\r\n");//发送USERsprintf(m_send_buffer, "USER %s\r\n", username);ret = ftp_send_command(m_send_buffer);if(ret != 1){socket_close(m_socket_cmd);return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 331){socket_close(m_socket_cmd);return 0;}//发送PASSsprintf(m_send_buffer, "PASS %s\r\n", password);ret = ftp_send_command(m_send_buffer);if(ret != 1){socket_close(m_socket_cmd);return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 230){socket_close(m_socket_cmd);return 0;}LOG_INFO("login success.\r\n");//设置为二进制模式ret = ftp_send_command("TYPE I\r\n");if(ret != 1){socket_close(m_socket_cmd);return 0;}ret = ftp_recv_respond(m_recv_buffer, 1024);if(ret != 200){socket_close(m_socket_cmd);return 0;}return 1;
}void ftp_quit(void)
{ftp_send_command("QUIT\r\n");socket_close(m_socket_cmd);
}void ftp_init(void)
{m_socket_cmd = socket_create();m_socket_data= socket_create();
}

2020.04.25补充:
补充socket.h的源码:
socket.h是网络编程的底层接口,不同的系统平台有不同的实现方法。因此需要自行实现这些函数。

#ifndef __SOCKET_H
#define __SOCKET_Hint  socket_create(void);
void socket_delete(int sock);
int  socket_connect(int sock, const char *addr, int port);
void socket_close(int sock);
int  socket_send(int sock, void *data, int len);
int  socket_recv(int sock, void *data, int len);#endif

补充应用代码:

//应用层下载文件的调用流程(仅供参考)
void main()
{int ret;ftp_init();ret = ftp_login("192.168.0.1", 21, "admin", "admin");int size = ftp_filesize("/dir/filename.bin");char *buf = malloc(size);ret = ftp_download("/dir/filename.bin", buf, size);ftp_quit();free(buf);
}

C语言实现的简易FTP客户端相关推荐

  1. 安卓端简易FTP客户端APP开发

    先上效果图: 本文的FTP客户端基于commons-net-3.3.jar库实现. 实现了ftp服务器登录. 单个文件的下载和上传,以及本地复制和删除文件. 一.登录服务器活动模块编写: 这块呢首先是 ...

  2. go语言实现简易ftp客户端

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/xiangxianghehe/article/details/78310249 Go语言实现的ftp库 ...

  3. 7个免费的Linux FTP客户端工具

    在Dropbox.YouSendIt.idrive以及许多这样云存储和共享工具的帮助下,我们在互联网上发送和共享大型文件变得容易起来.所有这些网站都可以帮助你在互联网上传送文件,但如果你要分享庞大的数 ...

  4. 应用层——使用 Socket 通信实现 FTP 客户端程序

    转自:http://blog.csdn.net/yixijide/article/details/8280263 简介: FTP 客户端如 FlashFXP,File Zilla 被广泛应用,原理上都 ...

  5. freecplus框架-ftp客户端

    文章目录 一.源代码说明 二.概述 三.Cftp类 四.准备测试环境 1.创建Linux操作系统用户 2.安装配置ftp服务器 3.配置防火墙 4.准备测试的文件 五.示例程序 1.获取服务器文件列表 ...

  6. 文件管理器和FTP客户端:ForkLift for Mac

    知名文件管理工具和FTP客户端Transmit for mac更新啦!这是一个功能强大的文件查找和替换工具和FTP + ***TP + WebD*** + Amazon S3的客户端,集齐批量重命名, ...

  7. C#毕业设计——基于C#+asp.net+FTP的FTP客户端设计与实现(毕业论文+程序源码)——FTP客户端

    基于C#+asp.net+FTP的FTP客户端设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于C#+asp.net+FTP的FTP客户端设计与实现,文章末尾附有本毕业设计的论文和源码下载地 ...

  8. FTP客户端设计与实现

    互联网的一大特点是实现信息共享,文件传输是信息共享的十分重要的内容之一.随之出现了许多FTP服务器来共享一些信息资源,编写一个操作简单,方便的FTP客户端来下载这些资源受到了人们的极大欢迎. FTP客 ...

  9. C/C++、Qt4实现FTP客户端(有无界面版)

    简介 操作系统:Ubuntu 12.04 LTS 开发工具:GNU4.6.3,C/C++标准库,Qt4,Qt Creator Documentation 2.4.1 代码下载:码云:传送门,GitHu ...

  10. filezilla检查新版本mac_FileZilla for Mac(ftp客户端工具)中文版

    原标题:FileZilla for Mac(ftp客户端工具)中文版 filezilla mac 中文版是一款运行在Mac OS平台上的FTP上传下载工具,filezilla mac版具备所有的FTP ...

最新文章

  1. iOS 实现点击微信头像效果
  2. 悉尼科技大学入选 CVPR 2021 的 9 篇论文,都研究什么?
  3. 开源(Open Source)那些事儿 (一)
  4. 【Android NDK 开发】NDK C/C++ 代码崩溃调试 - Tombstone 报错信息日志文件分析 ( 获取 tombstone_0X 崩溃日志信息 )
  5. 我的python渗透测试工具箱之自制netcat
  6. 数据库主从和主备部署介绍
  7. 520送你一份WebStorm的主题包,附带使用图文教程
  8. yeoman生成react基本架构
  9. 横流式冷却塔计算风量_10T-1000T冷却塔厂家批发零售
  10. 年夜饭之 -- 辣椒炒圣子
  11. 数据库原理—常用的DBS产品简介(六)
  12. Linux平台下卸载MySQL的方法
  13. 无法更新 TeamViewer 服务属性是什么意思?
  14. 使用WinSCP上传下载IPAD的文件
  15. 【图文详解】Python(3.9.6)安装教程 + pip安装教程
  16. 服务器中的软件如何备份文件夹在哪里找,itunes备份文件在哪,如何找到itunes的备份文件...
  17. 奇虎终于涉足生活搜索
  18. Ruby‘s Adventrue游戏制作笔记(十二)Unity给角色添加简单的特效
  19. TUV南德与重庆赛宝于四川签署合作协议并联合举办多国认证研讨会
  20. 【2016-05-09】程序员的日常:咖啡

热门文章

  1. github注册以及安装教程
  2. 使用pbrt遇到的问题及解决方法
  3. DDC及EDID内容简介
  4. multisim变压器反馈式_借助Multisim 10仿真的负反馈放大电路
  5. QT出现应用程序无法正常启动0xc000007b的错误
  6. xvidcore-1.3.2编译
  7. 【边尝试边写博客】入侵学校服务器
  8. hd获取硬盘序列号_获取硬盘序列号
  9. Windows内核面试题(持续更新,目前完成度30%约1.8万字)
  10. VMware Workstation16.2下载安装教程(win10)