几个月前按照网上的教程写了一个FTP的服务器,现在回头整理一下里面的一些知识。
FTP简介
FTP是文件传输协议(File Transfer Protocol),工作在TCP/IP协议族的应用层,其传输层使用的是TCP协议,它是基于客户/服务器模式工作的(C/S架构),TCP/IP协议中,FTP标准命令TCP端口号为21,Port方式数据端口为20。
FTP的两种传输方式
FTP有两种传输方式:ASCII文件传输模式和二进制文件传输模式。
ASCII文件和二进制文件的区别
ASCII文件也称为文本文件,ASCII文件和二进制文件在物理上没有本质的区别,他们都是由一系列的比特位组成的,他们的差别仅仅在于逻辑之上,或者说系统对他们的解析方法不同。ASCII文件由ASCII字符构成,一个ASCII字符有7个比特位,最高位总是0,而二进制文件的最高位有可能就是1。另一个差别是换行符,不同系统对换行符的表示方式不同,windows下换行符是\r\n,linux下的换行符是\n,其他的系统可能是其他的方式,ASCII传输方式和二进制传输方式在处理换行符也有所不同。
ASCII传输方式
假定用户正在拷贝的文件包含的简单ASCII码文本,如果在我们使用的系统是windows系统,而运行ftp服务器的远程机器上的系统是Linux,那么使用ASCII传输模式会将\r\n转为\n。反过来,如果我们使用的系统是Linux系统,而运行ftp服务器的远程机器上的系统是Windows,那么使用ASCII传输模式会将\n转为\r\n。也就是说,当文件传输时ftp通常会自动地调整文件的内容以便于把文件解释成另外那台计算机存储文本文件的格式。
二进制传输模式
FTP的两种工作方式
FTP的连接分为控制连接和数据连接,控制连接用于传输控制命令,是随客户端一同存在的,而数据连接只是短暂存在,每次要发生数据的时候才建立数据连接,数据传输完毕后就断开数据连接。FTP的控制连接总是由客户端想服务器端发起的,而FTP数据连接的建立有两种途径,一种是客户端连接到服务器端,另一种是服务器连接到客户端,这分别对应着FTP的两种工作模式:被动模式和主动模式。主动和被动主要是针对FTP服务器而言的,如果是FTP服务器主动连接到客户端则为主动模式,如果FTP服务器被动地接受客户端的连接请求则是被动模式。
主动模式
被动模式
FTP被动模式的工作过程如下:首先也是客户端向服务器端21端口发起连接,经过三次握手建立控制连接通道,
被动模式,需要进行数据传输的时候,客户端要向服务器发送一个PASV表示进行被动传输,即数据通道的建立是由客户端向服务器端发起的,而此时客户端需要知道连接到服务器的哪一个端口,于是服务器向客户端发送被动模式的端口XX,之后客户端向服务器的XX端口发起连接建立数据连接通道。其示意图如下:
那么FTP的数据连接为什么要分为主动模式和被动模式呢?这个和NAT或防火墙对主被动模式的影响有关。
NAT或防火墙对主被动模式的影响
FTP客户端处于NAT或防火墙之后的主动模式
FTP客户端处于NAT或防火墙之后的被动模式
如果FTP客户端处于NAT或防火墙之后,并且使用被动连接,此时是可以连接成功的,示意图如下图所示,过程和之前的类似,这里不再重复。
FTP服务器处于NAT或防火墙之后的被动模式
如果FTP服务器处于NAT或防火墙之后,在建立控制连接通道的时候,由于是客户端向服务器发起连接,因此需要在NAT配置一个映射条目(FTP服务器固定是21端口),经过三次握手建立控制连接通道。若使用被动模式,则在建立数据连接通道的时候,处于外网的客户端向处于内网的服务器发起的连接可能建立不能成功,这个和之前的也是类似的。所以我们可以知道,当FTP服务器处于NAT或防火墙之后的被动模式可能建立不成功。
FTP服务器处于NAT或防火墙之后的主动模式
FTP的进程模型
FTP服务器是采用多进程的方式实现的,而且每来一个连接,创建了两个进程来为这个连接服务。
为什么采用多进程
FTP服务器是绝对不能采用多线程的。假如现在我们使用的是多线程的方式,此时有三个用户(A,B,C)连接过来,则服务器创建了三个线程(A,B,C)来进行处理。此时,假设三个客户端登陆的是同一个用户LCW,并且A将目录切换到a,B将目录切换到b,C将目录切换到c,那么他们是会相互影响到的,因为多个线程是共享同一个工作目录的,当前线程对工作目录的修改回影响到其他的线程,这是不允许的,所以不能采用多线程的方式。同时IO复用也是不可以的(单线程,会影响其他连接)
为什么一个连接要用两个进程
那为什么一个客户端连接过来要采用两个进程呢?
这其实是出于一些安全性的考虑,一些权限方面的限制。假如此时有一个普通用户LCW连接过来,则当前进程会更改为LCW进程,而在要使用FTP的主动模式进行数据传输的时候,FTP要向客户端发起连接,FTP服务器需要绑定一个20端口,而LCW是一个普通的用户,没有权限进行端口的绑定,这就意味着他无法建立正常的数据通道。所以需要一个进程nobody进程,来协助LCW进程(服务进程)。还有在其他某些操作的时候,我们不希望客户端用拥有太大的权限,反正某些危险的操作,因此把需要特殊权限的操作都交由nobody进程来完成,而服务进程只是完成和客户端的通信。
LCW进程称为服务进程,主要是用来实现与客户端进行通信,而nobody进程为协助进程,主要协助LCW进程完成一些和特殊权限相关的操作,因而一个连接需要使用两个进程来服务。其进程模式如下图所示: