标题:myftp: 一个linux下简单的ftp客户端实现 出处:Felix021 时间:Mon, 13 Apr 2009 17:24:30 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?1552 内容: 因为网络程序设计课程要求做一个扩展型的作业,其中一个选项是ftp client。于是我就选了这个。 做完以后发现其实不难,而最难的地方,在于了解FTP协议的工作过程。 下面随便闲扯一点东西吧。 在网络上,经常遇到的应用层协议包括HTTP,FTP,SMTP,POP3,都使用TCP协议连接,感觉挺像的。 HTTP协议显然是最常见的,在客户端和服务器之间只有一条连接,既传输控制命令(HTTP头),也传输数据(HTTP实体) 因此在编写动态网页脚本的时候,一定要先输出header,然后才输出HTML代码。 FTP协议则采用两条连接,一条控制连接,一条数据连接。 控制连接用于传输控制命令,始终保持连接,直到客户端发出QUIT命令结束连接; 数据连接在需要传输数据的时候打开(发起端可以是客户端也可以是服务器),传输完毕后就断开。 FTP的数据连接有两种模式,一种是主动模式,一种是被动模式(PASV, Passive Mode)。 采用主动模式时,客户端监听一个本地端口,由服务器发起连接传输数据; 而采用被动模式时,则是由服务器打开一个端口,客户端发起连接接收数据。 下面我用telnet来模拟一个FTP客户端连接、登陆、使用被动模式传输文件并退出的完整过程: 在我的机器上,环境是Ubuntu 8.10 + vsftpd (@ Port 21) 约定一下格式:无特殊前缀的行是telnet输出的 $ 开头的行表示输入命令,<- 开头的行表示是收到的消息, -> 开头的行表示是手工输入的消息,//开头的行是felix写的说明 //打开一个terminal,windows下打开命令提示符也是一样的,我们暂且称其为tty1 $ telnet localhost 21 Trying 127.0.0.1... Connected to felix021-laptop. Escape character is '^]'. <- 220 Welcome to felix's FTP service. //到这里可以看到,telnet已经连接到localhost的21号端口,服务器返回一个消息 //消息的格式是 220 ooxx,220大概表示一切OK,你可以开始登录了 //而ooxx则是一个简单的消息,是可以由管理员修改配置的 //注意:可能服务器会返回多行220消息 -> USER ooxx <- 331 Please specify the password. //说明用户名OK,等你输入密码 -> PASS xxoo //故意输入一个错误的密码 <- 530 Login incorrect. // 小样,密码错了就不让你登录,重新来 -> USER ooxx <- 331 Please specify the password. -> PASS ooxx <- 230 login successful. // OK,登上去了 -> PWD 257 "/home/ooxx" // 当前我们在服务器的 /home/ooxx 目录下,下面我们看看这个目录下面有什么 // 因为目录的内容属于数据连接的,所以需要进入被动模式 -> PASV <- 227 Entering Passive Mode (127,0,0,1,160,231) //注意,服务器返回了6个[用逗号分隔的]数字 //前四个数字很容易认出吧?127.0.0.1,就是IP地址 //后面俩呢?就是端口号了,计算方法是 160 * 256 + 231 = 41191 -> LIST //现在被阻塞了,因为服务器等着你连接它的41191端口接数据呢 //现在打开一个新的terminal,我们暂且称其为tty2 //@ tty2 $ telnet localhost 41191 Trying 127.0.0.1... Trying 127.0.0.1... telnet: Unable to connect to remote host: Connection refused //哎呀不好意思,我动作太慢了,服务器等不及了, //下面回到tty1,重来 //@ tty1 -> PASV <- 227 Entering Passive Mode (127,0,0,1,73,55) //PORT = 18743 -> LIST //阻塞了,来,我们到tty2看看 //@ tty2 $ telnet localhost 18743 Trying 127.0.0.1... Connected to felix021-laptop. Escape character is '^]'. <- drwxr-xr-x 2 1001 1001 4096 Apr 10 16:55 1"2 <- lrwxrwxrwx 1 1001 1001 26 Apr 10 06:09 Examples -> /usr/share/example-content <- -rw-r--r-- 1 1001 1001 4744 Apr 10 08:49 main.cpp <- drwxr-xr-x 2 1001 1001 4096 Apr 10 07:58 ooxx <- -rw-r--r-- 1 1001 1001 2647 Apr 12 07:48 tags Connection closed by foreign host. //哗地一下刷过去这么多东西,不过稍微看一下,就会明白的吧:) //咱再回到tty1看看 //@ tty1 <- 150 Here comes the directory listing. <- 226 Directory send OK. -> SIZE tags //看看tags这个文件有多大 <-213 2647 //嗯,2647个字节,很好,这下我们可以下载文件了 //还是用被动模式(每次传输数据都要重新设置传输方式) -> PASV <- 227 Entering Passive Mode (127,0,0,1,34,112) //PORT = 8816 -> RETR tags //阻塞了,我们切换到tty2,来看看tags文件的内容是啥 //@ tty2 $ telnet localhost 8816 Trying 127.0.0.1... Connected to felix021-laptop. Escape character is '^]'. <- ooxx ooxx ooxx ooxx Connection closed by foreign host. //好吧,我承认tags文件的内容不应该只有这么几个ooxx,不过它太长了,放在这里影响阅读不是~ // @ tty1 <- 150 Opening BINARY mode data connection for tags (2647 bytes). <- 226 File send OK. -> QUIT <- 221 Goodbye. //byebye~ 好了,以上就是ftp客户端以被动模式向服务器请求一个文件的全过程。 主动模式稍稍有点不同,在需要传输数据的时候,需要先建立一个socket监听本地的某个端口P 计算出a, b满足P = a * 256 + b,然后使用PORT命令,后面跟上类似127,0,0,1,a,b的地址信息 然后使用TYPE I命令设置传输模式为BINARY(默认为ASCII)。 由于不能用telnet监听端口,所以主动模式的就不能使用telnet模拟了。 注意,由于ftp服务器程序的不同,输出的提示字符格式可能不同,但是状态码一定会按照RFC标准给出的。 下面附上我自己写的ftp client吧。仅供参考:因为只完成了PASV模式,能对付我机器上的vsftp,不能保证通用性=.= p.s. 注意一点:在程序中编写需要传输给服务器的命令时,命令结尾必须加上一个\r\n (回车换行) 表示这是一行命令的结束。 再p.s. 我很不希望这个东西会被别人当作大作业交给老师。 @2009-06-02 再再p.s. 已经出了跨平台可编译版本哦,详见 myftp跨平台版本 点击这里下载文件 最后:如果你要自己写一个客户端,并且想要实现更多命令,你可以使用 ftp -d localhost 来连接服务器 -d表示debug模式,所有在发送和接受到的控制命令都会显示出来,这样你就能看到你要实现的功能需要什么命令。 linux和windows下的ftp都支持-d参数。 Generated by Bo-blog 2.1.0