您的位置:首页 > 编程语言 > PHP开发

FTP-------应用层协议

2016-07-10 19:30 537 查看
FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。
基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:"下载"
(Download)和"上传"(Upload)。"下载"文件就是从远程主机拷贝文件至自己的计算机上;"上传"文件就是将文件从自己的计算机中拷贝至
远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。

FTP服务器

与大多数Internet服务一样,FTP也是一个客户机/服务器系统
用户通过一个支持FTP协议的客户机程序,连接到在远程主机上的FTP服务器程序。用户通过客户机程序向服务器程序发出命令,服务器程序执行用户所发出的
命令,并将执行的结果返回到客户机。比如说,用户发出一条命令,要求服务器向用户传送某一个文件的一份拷贝,服务器会响应这条命令,将指定文件送至用户的
机器上。客户机程序代表用户接收到这个文件,将其存放在用户目录中。

1)用户授权

要连上 FTP 服务器(即“登陆”),必须要有该 FTP 服务器授权的帐号,也就是说你只有在有了一个用户标识和一个口令后才能登陆FTP服务器,享受FTP服务器提供的服务。
2)FTP地址格式
FTP地址如下: ftp://用户名:密码@FTP服务器IP或域名:FTP命令端口/路径/文件名 上面的参数除FTP服务器IP或域名为必要项外,其他都不是必须的。如以下地址都是有效FTP地址: ftp://foolish.6600.org ftp://list:list@foolish.6600.org ftp://list:list@foolish.6600.org:2003 ftp://list:list@foolish.6600.org:2003/soft/list.txt

匿名FTP

使用FTP时必须首先登录,在远程主机上获得相应的权限以后,方可下载或上传文件。也就是说,要想同哪一台计算机传送文件,就必须具有哪一台计算机的适当授权。换言之,除非有用户ID和口令,否则便无法传送文件。这种情况违背了Internet的开放性,Internet上的FTP主机何止千万,不可能要求每个用户在每一台主机上都拥有帐号。匿名FTP就是为解决这个问题而产生的。
匿名FTP是这样一种机制,用户可通过它连接到远程主机上,并从其下载文件,而无需成为其注册用户。系统管理员建立了一个特殊的用户ID,名为anonymous, Internet上的任何人在任何地方都可使用该用户ID。
通过FTP程序连接匿名FTP主机的方式同连接普通FTP主机的方式差不多,只是在要求提供用户标识ID时必须输入anonymous,该用户ID的口令可以是任意的字符串。习惯上,用自己的E-mail地址作为口令,使系统维护程序能够记录下来谁在存取这些文件。
值得注意的是,匿名FTP不适用于所有Internet主机,它只适用于那些提供了这项服务的主机。
当远程主机提供匿名FTP服务时,会指定某些目录向公众开放,允许匿名存
取。系统中的其余目录则处于隐匿状态。作为一种安全措施,大多数匿名FTP主机都允许用户从其下载文件,而不允许用户向其上传文件,也就是说,用户可将匿
名FTP主机上的所有文件全部拷贝到自己的机器上,但不能将自己机器上的任何一个文件拷贝至匿名FTP主机上。即使有些匿名FTP主机确实允许用户上传文
件,用户也只能将文件上传至某一指定上传目录中。随后,系统管理员会去检查这些文件,他会将这些文件移至另一个公共下载目录中,供其他用户下载,利用这种方式,远程主机的用户得到了保护,避免了有人上传有问题的文件,如带病毒的文件。
默认情况下FTP协议使用TCP端口中的
20和21这两个端口,其中20用于传输数据,21用于传输控制信息。但是,是否使用20作为传输数据的端口与FTP使用的传输模式有关,如果采用主动模
式,那么数据传输端口就是20;如果采用被动模式,则具体最终使用哪个端口要服务器端和客户端协商决定。

FTP协议的任务是从一台计算机将文件传送到另一台计算机,它与这两台计算机所处的位置、联接的方式、甚至是是否使用相同的操作系统无关。假设两台计算机
通过ftp协议对话,并且能访问Internet,你可以用ftp命令来传输文件。每种操作系统使用上有某一些细微差别,但是每种协议基本的命令结构是相
同的。

传输方式

FTP的传输有两种方式:ASCII、二进制。
ASCII传输方式
假定用户正在拷贝的文件包含的简单ASCII码文本,如果在远程机器上运行的不是UNIX,当文件传输时ftp通常会自动地调整文件的内容以便于把文件解释成另外那台计算机存储文本文件的格式。
但是常常有这样的情况,用户正在传输的文件包含的不是文本文件,它们可能是程序,数据库,字处理文件或者压缩文件。在拷贝任何非文本文件之前,用binary 命令告诉ftp逐字拷贝。
二进制传输模式
在二进制传输中,保存文件的位序,以便原始和拷贝的是逐位一一对应的。即使目的地机器上包含位序列的文件是没意义的。例如,macintosh以二进制方式传送可执行文件到Windows系统,在对方系统上,此文件不能执行。
如在ASCII方式下传输二进制文件,即使不需要也仍会转译。这会损坏数据。(ASCII方式一般假设每一字符的第一有效位无意义,因为ASCII字符组合不使用它。如果传输二进制文件,所有的位都是重要的。)

支持模式

FTP支持两种模式:Standard (PORT方式,主动方式),Passive (PASV,被动方式)。
Port模式
FTP 客户端
先和服务器的TCP
21端口建立连接,用来发送命令,客户端需要接收数据的时候在这个通道上发送PORT命令。PORT命令包含了客户端用什么端口接收数据。在传送数据的时
候,服务器端通过自己的TCP 20端口连接至客户端的指定端口发送数据。FTP server必须和客户端建立一个新的连接用来传送数据。
Passive模式
建立控制通道和Standard模式类似,但建立连接后发送Pasv命
令。服务器收到Pasv命令后,打开一个临时端口(端口号大于1023小于65535)并且通知客户端在这个端口上传送数据的请求,客户端连接FTP服务
器此端口,然后FTP服务器将通过这个端口传送数据。
很多防火墙
设置的时候都是不允许接受外部发起的连接的,所以许多位于防火墙后或内网的FTP服务器不支持PASV模式,因为客户端无法穿过防火墙打开FTP服务器的
高端端口;而许多内网的客户端不能用PORT模式登陆FTP服务器,因为从服务器的TCP 20无法和内部网络的客户端建立一个新的连接,造成无法工作。
主动和被动模式FTP有两种使用模式:主动和被动。主动模式要求客户端和服务器端同时打开并且监听一个端口以建立连接。在这种情况下,客户端由于安
装了防火墙会产生一些问题。所以,创立了被动模式。被动模式只要求服务器端产生一个监听相应端口的进程,这样就可以绕过客户端安装了防火墙的问题。

  一个主动模式的FTP连接建立要遵循以下步骤:

  1.客户端打开一个随机的端口(端口号大于1024,在这里,我们称它为x),同时一个FTP进程连接至服务器的21号命令端口。此时,源端口为随机端口x,在客户端,远程端口为21,在服务器。

  2.客户端开始监听端口(x+1),同时向服务器发送一个端口命令(通过服务器的21号命令端口),此命令告诉服务器客户端正在监听的端口号并且已准备好从此端口接收数据。这个端口就是我们所知的数据端口。

  3.服务器打开20号源端口并且建立和客户端数据端口的连接。此时,源端口为20,远程数据端口为(x+1)。

  5.客户端通过本地的数据端口建立一个和服务器20号端口的连接,然后向服务器发送一个应答,告诉服务器它已经建立好了一个连接。
FXP
FXP说简单点就是一个FTP客户端控制两个FTP服务器,在两个FTP服务器之间传送文件。FXP的全称为File Exchange
Protocol――文件交换协议,可以认为FXP本身其实就是FTP的一个子集,因为FXP方式实际上就是利用了FTP服务器的Proxy命令,不过它
的前提条件是FTP服务器要支持PASV,且支持FXP方式。FXP传送时,文件并不下载至本地,本地只是发送控制命令,故FXP传送时的速度只与两个
FTP服务器之间的网络速度有关,而与本地速度无关。因FXP方式本地只发送命令,故在开始传送后,只要本地不发送停止的命令,就算是本地关机了,FXP
仍在传送,直至一个文件传送完成或文件传送出错后,FTP服务器等待本地发送命令时,才会因不能接收到命令而终止FXP传送。
因为上述的原因,FXP传送出错时,本地的用户进程还留在FTP服务器中,并没有退出,如此时再次连接FTP服务器,可能会因用户线程超过允
许,FTP服务器提示客户已登陆并拒绝客户端的连接,直至服务器中的傀儡进程因超时或其他原因被FTP服务器杀死后,才能再次连接FTP服务器。
成功FXP有两个必要条件:①两个FTP服务器均支持FXP;②两个FTP服务器均支持PASV方式。但并不是说满足这两个条件的FTP服务器均经
本地操作成功FXP,这还与本地与FTP服务器的网络状况有关。故有时会出现同样两个FTP,别人可以FXP,而你不可以的情况。

TFTP
TFTP(Trivial File Transfer Protocol)小文件传输协议

它是一个网络应用程序,它比FTP简单也比FTP功能少。它在不需要用户权限或目录可见的情况下使用,它使用UDP协议而不是TCP协议。
TFTP是一个传输文件的简单协议,它基于UDP协议而实现,但是我们也不能确定有些TFTP协议是基于其它传输协议完成的。此协议设
计的时候是进行小文件传输的,因此它不具备通常的FTP的许多功能,它只能从文件服务器上获得或写入文件,不能列出目录,不进行认证,它传输8位数据。传
输中有三种模式:netascii,这是8位的ASCII码形式,另一种是octet,这是8位源数据类型;最后一种mail已经不再支持,它将返回的数
据直接返回给用户而不是保存为文件。   
任何传输起自一个读取或写入文件的请求,这个请求也是连接请求。如果服务器批准此请求,则服务器打开连接,数据以定长512字节传输。
每个数据包包括一块数据,服务器发出下一个数据包以前必须得到客户对上一个数据包的确认。如果一个数据包的大小小于512字节,则表示传输结构。如果数据
包在传输过程中丢失,发出方会在超时后重新传输最后一个未被确认的数据包。通信的双方都是数据的发出者与接收者,一方传输数据接收应答,另一方发出应答接
收数据。大部分的错误会导致连接中断,错误由一个错误的数据包引起。这个包不会被确认,也不会被重新发送,因此另一方无法接收到。如果错误包丢失,则使用
超时机制。错误主要是由下面三种情况引起的:不能满足请求,收到的数据包内容错误,而这种错误不能由延时或重发解释,对需要资源的访问丢失(如硬盘满)。
TFTP只在一种情况下不中断连接,这种情况是源端口不正确,在这种情况下,指示错误的包会被发送到源机。这个协议限制很多,这是都是为了实现起来比较方
便而进行的。   
Linux下常用FTP命令:
1. 连接ftp服务器
格式:ftp [hostname| ip-address]
a)在linux命令行下输入:ftp 192.168.1.1
b)服务器询问你用户名和密码,分别输入用户名和相应密码,待认证通过即可。2. 下载文件下载文件通常用get和mget这两条命令。
a) get
格式:get [remote-file] [local-file]
将文件从远端主机中传送至本地主机中。
如要获取远程服务器上/usr/your/1.htm,则ftp> get /usr/your/1.htm 1.htm (回车)

b) mget      
格式:mget [remote-files]
从远端主机接收一批文件至本地主机。
如要获取服务器上/usr/your/下的所有文件,则ftp> cd /usr/your/
ftp> mget *.* (回车)

此时每下载一个文件,都会有提示。如果要除掉提示,则在mget *.* 命令前先执行:prompt off注意:文件都下载到了linux主机的当前目录下。比如,在 /usr/my下运行的ftp命令,则文件都下载到了/usr/my下。3.上传文件a) put
格式:put local-file [remote-file]
将本地一个文件传送至远端主机中。
如要把本地的1.htm传送到远端主机/usr/your,并改名为2.htmftp> put 1.htm /usr/your/2.htm (回车)

b) mput
格式:mput local-files
将本地主机中一批文件传送至远端主机。
如要把本地当前目录下所有html文件上传到服务器/usr/your/ 下ftp> cd /usr/your (回车)
ftp> mput *.htm (回车)

注意:上传文件都来自于主机的当前目录下。比如,在 /usr/my下运行的ftp命令,则只有在/usr/my下的文件linux才会上传到服务器/usr/your 下。4. 断开连接bye:中断与服务器的连接。ftp> bye (回车)
实现编码前需知:

相比其他协议,如 HTTP 协议,FTP 协议要复杂一些。与一般的 C/S 应用不同点在于一般的C/S 应用程序一般只会建立一个 Socket 连接,这个连接同时处理服务器端和客户端的连接命令和数据传输。而FTP协议中将命令与数据分开传送的方法提高了效率。
FTP
使用 2 个端口,一个数据端口和一个命令端口(也叫做控制端口)。这两个端口一般是21 (命令端口)和 20 (数据端口)。控制 Socket
用来传送命令,数据 Socket 是用于传送数据。每一个 FTP 命令发送之后,FTP
服务器都会返回一个字符串,其中包括一个响应代码和一些说明信息。其中的返回码主要是用于判断命令是否被成功执行了。

命令端口

一般来说,客户端有一个 Socket 用来连接 FTP 服务器的相关端口,它负责 FTP 命令的发送和接收返回的响应信息。一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。

数据端口

对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个 Socket来完成。
如果使用被动模式,通常服务器端会返回一个端口号。客户端需要用另开一个 Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。
如果使用主动模式,通常客户端会发送一个端口号给服务器端,并在这个端口监听。服务器需要连接到客户端开启的这个数据端口,并进行数据的传输。

主要用到的 FTP 命令

FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "\r\n"结束。
要下载或上传一个文件,首先要登入 FTP 服务器,然后发送命令,最后退出。这个过程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。
USER: 指定用户名。通常是控制连接后第一个发出的命令。“USER gaoleyi\r\n”: 用户名为gaoleyi 登录。
PASS: 指定用户密码。该命令紧跟 USER 命令后。“PASS gaoleyi\r\n”:密码为 gaoleyi。
SIZE: 从服务器上返回指定文件的大小。“SIZE file.txt\r\n”:如果 file.txt 文件存在,则返回该文件的大小。
CWD: 改变工作目录。如:“CWD dirname\r\n”。
PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV\r\n”。
PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器采用主动模式连接客户端。如:“PORT h1,h2,h3,h4,p1,p2”。
RETR: 下载文件。“RETR file.txt \r\n”:下载文件 file.txt。
STOR: 上传文件。“STOR file.txt\r\n”:上传文件 file.txt。
REST: 该命令并不传送文件,而是略过指定点后的数据。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100\r\n”:重新指定文件传送的偏移量为 100 字节。
QUIT: 关闭与服务器的连接。
以命令方式从ftp服务器下载文件:

进入ftp在命令提示符内输入”ftp“并回车,进入ftp提示符ftp>

在ftp>输入open 192.168.80.17回车;/*如果你的FTP服务器不是用的21默认端口,假如端口是10021,那么此步的命令应在后面空格加10021,即“open 192.168.80.17 10021“*/

用户/*提示输入用户名*/

密码/*提示输入密码,密码不会显示,输完密码后回车即可*/

dir/*成功登陆后就可以用dir查看FTP服务器中的文件目录*/

lcd d:\test/*定位本地默认文件夹,在前面事先在D:盘创建好的*/

!dir/*查看本地文件夹中有文件及目录*/

prompt/*打开和关闭交互模式,关闭交互模式时使用mget等不会提示*/

mget *.*(下载)/*将FTP服务器默认目录中的所有文件下载到当前目录下(d:\test) */

bye/*退出FTP服务器*/上传下载时特别要注意服务器及本地电脑的当前目录,交件是从哪里到哪里的问题。查看FTP服务器的当前目录命令为pwd,可以用cd命令定位服务器目录。可以用lcd命令定位本地电脑的目录。Mkdir创建远程文件夹

FTP 响应码

客户端发送 FTP 命令后,服务器返回响应码。
响应码用三位数字编码表示:
第一个数字给出了命令状态的一般性指示,比如响应成功、失败或不完整。
第二个数字是响应类型的分类,如 2 代表跟连接有关的响应,3 代表用户认证。
第三个数字提供了更加详细的信息。
第一个数字的含义如下:
1 表示服务器正确接收信息,还未处理。
2 表示服务器已经正确处理信息。
3 表示服务器正确接收信息,正在处理。
4 表示信息暂时错误。
5 表示信息永久错误。
第二个数字的含义如下:
0 表示语法。
1 表示系统状态和信息。
2 表示连接状态。
3 表示与用户认证有关的信息。
4 表示未定义。
5 表示与文件系统有关的信息。

Socket 编程的几个重要步骤

Socket 客户端编程主要步骤如下:
socket() 创建一个 Socket

connect() 与服务器连接

write() 和 read() 进行会话

close() 关闭 Socket

Socket 服务器端编程主要步骤如下:
socket() 创建一个 Socket

bind()

listen() 监听

accept() 接收连接的请求

write() 和 read() 进行会话

close() 关闭 Socket




























实现 FTP 客户端上传下载功能

下面让我们通过一个例子来对 FTP 客户端有一个深入的了解。本文实现的 FTP 客户端有下列功能:
客户端和 FTP 服务器建立 Socket 连接。

向服务器发送 USER、PASS 命令登录 FTP 服务器。

使用 PASV 命令得到服务器监听的端口号,建立数据连接。

使用 RETR/STOR 命令下载/上传文件。

在下载完毕后断开数据连接并发送 QUIT 命令退出。

在整个交互的过程中,控制连接始终处于连接的状态,数据连接在每传输一个文件时先打开,后关闭。

客户端和 FTP 服务器建立 Socket 连接

当客户端与服务器建立连接后,服务器会返回 220 的响应码和一些欢迎信息。

客户端登录 FTP 服务器

当客户端发送用户名和密码,服务器验证通过后,会返回 230 的响应码。然后客户端就可以向服务器端发送命令了。

客户端让 FTP 服务器进入被动模式

当客户端在下载/上传文件前,要先发送命令让服务器进入被动模式。服务器会打开数据端口并监听。并返回响应码 227 和数据连接的端口号。

客户端通过被动模式下载文件

当客户端发送命令下载文件。服务器会返回响应码 150,并向数据连接发送文件内容。

客户端退出服务器

当客户端下载完毕后,发送命令退出服务器,并关闭连接。服务器会返回响应码 200。
需要注意的是发送 FTP 命令的时候,在命令后要紧跟 “\r\n”,否则服务器不会返回信息。回车换行符号 “\r\n” 是 FTP 命令的结尾符号,当服务器接收到这个符号时,认为客户端发送的命令已经结束,开始处理。否则会继续等待。

客户端通过被动模式向服务器上传文件

当客户端发送命令上传文件,服务器会从数据连接接收文件。

客户端通过主动模式向服务器上传文件

到目前为止,本文介绍的都是客户端用被动模式进行文件的上传和下载。下面将介绍客户端用主动模式下载文件。
主动模式:



被动模式:



代码实现:
//my_ftp.h
#include<iostream>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
using namespace std;
//my_ftp.cpp
#include"my_ftp.h"
#include"my_ftp.h"
const int SIZE=1024;
int interact(int sock,const char* input,char* output)
{
int ret=send(sock,input,strlen(input),0);
if(ret<0)
{
perror(strerror(errno));
return ret;
}
ret=recv(sock,output,SIZE,0);
if(ret<0)
{
perror(strerror(errno));
return ret;
}
output[ret]='\0';
return 0;
}
int start_listen(int sock,sockaddr_in& local)
{
int server=socket(AF_INET,SOCK_STREAM,0);
if(server<0)
{
perror(strerror(errno));
return -1;
}
socklen_t len=sizeof(local);
int ret=getsockname(sock,(sockaddr*)&local,&len);
if(ret<0)
{
perror("getsockname");
return -1;
}
local.sin_port=0;//random
ret=bind(server,(sockaddr*)&local,sizeof(local));
if(ret<0)
{
perror("bind");
return -1;
}
ret=listen(server,5);
if(ret<0)
{
perror("listen");
return -1;
}
ret=getsockname(server,(sockaddr*)&local,&len);
if(ret<0)
{
perror("getsockname");
return -1;
}
return server;
}
int recv_data(int data_sock,const char* filename)
{
FILE* fp=fopen(filename,"w");
if(fp==NULL)
{
perror("fopen");
return -1;
}
ssize_t _s=-1;
while(1)
{
char buf[SIZE];
_s=recv(data_sock,buf,SIZE,0);
if(_s<0)
{
perror("recv");
close(data_sock);
return _s;
}
else if(_s==0)
{
close(data_sock);
fclose(fp);
return 0;
}
if(fwrite(buf,_s,1,fp)!=1)
{
close(data_sock);
fclose(fp);
return -1;
}
}
}
int main(int argc,char* argv[])
{
if(argc!=5)
{
cout<<"Usage: [ip] [user] [pass] [filename]"<<endl;
return -1;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return -1;
}
sockaddr_in server_addr;
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=inet_addr(argv[1]);
server_addr.sin_port=htons(21);
int ret=connect(sock,(sockaddr*)&server_addr,sizeof(server_addr));
if(ret<0)
{
perror("connect");
return 2;
}
char buf[SIZE];
char output[SIZE];
ssize_t _s=recv(sock,buf,SIZE,0);
if(_s<0)
{
perror("recv");
return _s;
}
buf[_s]='\0';
cout<<buf<<endl;
sprintf(buf,"USER %s\r\n",argv[2]);
if(interact(sock,buf,output)<0)
{
perror("USER");
return -1;
}
cout<<output<<endl;
sprintf(buf,"PASS %s\r\n",argv[3]);
if(interact(sock,buf,output)<0)
{
perror("PASS");
return -1;
}
cout<<output<<endl;

sockaddr_in local_addr;
int listen_sock=start_listen(sock,local_addr);
if(listen_sock<0)
{
perror("start_listen");
return -1;
}
unsigned char* _ip=(unsigned char*)&local_addr.sin_addr.s_addr;
unsigned short _port=ntohs(local_addr.sin_port);
sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\r\n", _ip[0], _ip[1], _ip[2],_ip[3],_port/256,_port%256);
cout<<buf<<endl;
if(interact(sock,buf,output)<0)
{
cout<<"interact"<<endl;
return -1;
}
cout<<output<<endl;
sprintf(buf,"RETR %s\r\n",argv[4]);
if(interact(sock,buf,output)<0)
{
cout<<"interact"<<endl;
return -1;
}
cout<<output<<endl;
sockaddr_in ser_addr;
socklen_t ser_len=sizeof(ser_addr);
int data_sock=accept(listen_sock,(sockaddr*)&ser_addr,&ser_len);
if(data_sock<0)
{
perror("accept");
return -1;
}
close(listen_sock);
ret=recv_data(data_sock,argv[4]);
if (ret < 0) {
cout << "recv data error" << endl;
}
close(sock);
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  FTP