已经好久没有写过博客进行分享了。具体原因,在以后说。
这几天在了解FTP协议,准备任务是写一个FTP客户端程序。直接上干货了。
0.了解FTP作用
就是一个提供一个文件的共享协议。
1.了解FTP协议
FTP有指令和响应码。FTP 控制帧即指 TELNET 交换信息,包含 TELNET 命令和选项。然而,大多数 FTP 控制帧是简单的 ASCII 文本,可以分为 FTP 命令或 FTP 消息。 FTP 消息是对 FTP 命令的响应,它由带有解释文本的应答代码构成。
像这种利用交换信息来进行简单的控制,这种协议,还真的很好玩的说。 命令与响应码部分信息如下
2. 安装一个FTP服务器
我们先安装一个FTP服务器,用于测试,这里是用FileZilla Server作为FTP服务器。
启动后,增加一个用户user/user
3.FTP客户端源代码讲解
下面这个是FTPAPI.h文件
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 #ifndef FTPAPI_H_INCLUDED 2 #define FTPAPI_H_INCLUDED 3 4 #include <stdio.h> 5 #include <winsock2.h> 6 7 SOCKET socket_connect(char *host, int port); 8 SOCKET connect_server(char *host, int port); 9 int ftp_sendcmd_re(SOCKET sock, char *cmd, char *re_buf, ssize_t *len); 10 int ftp_sendcmd(SOCKET sock, char *cmd); 11 int login_server(SOCKET sock, char *user, char *pwd); 12 void socket_close(int c_sock); 13 14 15 /**********可用命令*********/ 16 SOCKET ftp_connect(char *host, int port, char *user, char *pwd); //连接到服务器 17 int ftp_quit(SOCKET sock); //断开连接 18 int ftp_type(SOCKET sock, char mode); //设置FTP传输类型 19 int ftp_cwd(SOCKET sock, char *path); //更改工作目录 20 int ftp_cdup(SOCKET sock); //回到上级目录 21 int ftp_mkd(SOCKET sock, char *path); //创建目录 22 SOCKET ftp_pasv_connect(SOCKET c_sock); //连接到PASV接口 23 int ftp_list(SOCKET c_sock, char *path, char **data, int *data_len); //列出FTP工作空间的所有目录 24 int ftp_deletefolder(SOCKET sock, char *path); //删除目录 25 int ftp_deletefile(SOCKET sock, char *filename); //删除文件 26 int ftp_renamefile(SOCKET sock, char *s, char *d); //修改文件/目录&移动文件/目录 27 int ftp_server2local(SOCKET c_sock, char *s, char *d, int * size); //从服务器复制文件到本地 RETR 28 int ftp_local2server(SOCKET c_sock, char *s, char *d, int * size); //从本地复制文件到服务器 STOR 29 int ftp_recv(SOCKET sock, char *re_buf, ssize_t *len); //获取响应码 30 31 32 #endif // FTPAPI_H_INCLUDED
下面这个是FTPResponseCode.h 文件 是对应答码简单的描述
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
下面这些是FTPAPI.cpp文件的函数代码
创建一个socket连接并返回socket套接字 socket_connect
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
连接到一个ftp服务器 connect_server
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
send发送命令,并返回recv结果 ftp_sendcmd_re
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
send发送命令 ftp_sendcmd
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
登录FTP服务器 login_server
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
winsock使用后,要调用WSACleanup函数关闭网络设备 socket_close
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
连接到FTP服务器 ftp_connect
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
断开FTP服务器 ftp_quit
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
设置FTP传输类型 A:ascii I:Binary ftp_type
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
更改工作目录 ftp_cwd
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
回到上级目录 ftp_cdup
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
创建目录 ftp_mkd
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
连接到PASV接口 ftp_pasv_connect
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
列出FTP工作空间的所有目录 ftp_list
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
删除目录 ftp_deletefolder
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
删除文件 ftp_deletefile
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
修改文件名&移动目录 ftp_renamefile
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
从服务器复制文件到本地 RETR ftp_server2local
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
从本地复制文件到服务器 STOR ftp_local2server
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
获取一行响应码 ftp_recv
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
4.测试ftp客户端
下载文件到本地
1 SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登录到FTP服务器 2 int ret = ftp_server2local(s,"user/user.zip","bin.zip",&size); //在FTP服务器获取文件 3 ftp_quit(s); //退出FTP服务器
上传文件到服务器
1 SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登录到FTP服务器 2 int ret = ftp_local2server(s,"user/user.zip","bin.zip",&size); //发送文件到FTP服务器 3 ftp_quit(s); //退出FTP服务器
下面这个是服务器的日志信息
下面这个是程序打印的调试信息
5.后话
到这里这个简单的ftp库就可以实现绝大部分的客户端功能了,但是这里面有一个问题,就是ftp是明文传输用户名/密码的,如果ftp上的文件比较重要的话,那么就有点问题了。当然这个不是本次的关注点,本次主要是了解ftp协议,还有从代码中了解这种交换控制命令的方法是一种很不错的技术手段,虽然这种方法已经是好多年前的,不安全,也过时了。但还是有可学的地方。
6.附录
下面这个附录是利用wireshark进行本地网络抓包测试。
1、抓包,要看部署点,在路由器、交换机等设备上做端口镜像、或分光口,或是接HUB、TAP等设备就可以直接获得通过这些口的报文。
2、抓包,也可在以局域网部署相关的网管软件或黑客工具(比如cain),可以用arp骗方式,让你的数据先发送到监控机上,然后再转发走。。这样你的数据就。。建议:
1、建议在电脑上打开ARP防护功能
2、在使用中尽量使用加密传输的工具,比如SSH、SSL、QQ一类的东西。可避免一些危害.
注意wireshark是不能抓取本地回环地址的数据包的,所以我以远程ftp服务器进行测试
这里是通过浏览器进行连接的。wireshark 1.12.4 从上面可以看到的信息 29-44这些表示了,浏览器一开始使用匿名进行登录,发现登录不上,所以请求用户名登录在81 82 84 85这4行中我们可以分析到,我是输入用户名user 密码user进行登录的,第106行表示用户名/密码错误。 如果是230 Login in 就表示成功登录了。如果我们捉到了这些信息,那么我们就可以进行登录了。这样就不安全了。既然ftp这么不安全为什么那么多地方用到ftp共享文件。这个就要说到ftp的作用了,ftp作用本来就是共享文件,所以安全性就不是很重要了。 至于加密方式以后再讲。
(开发环境mignw 编译的时候要加入libws2_32.a 这个库, 编译命令 g++ ftpapi.cpp -c -o ftpapi.o -lws2_32)
参考资料
TanHao的 THFTPAPI.c 文件 http://www.tanhao.me
文件下载 ftpapi.zip http://files.cnblogs.com/files/wunaozai/ftpapi20150512.zip
http://www.cnblogs.com/wunaozai/p/4494536.html