FTP只通过TCP连接,FTP不同于其他服务的是它使用了两个端口,一个数据端口和一个命令端口(或称为控制端口)。
通常21端口是命令端口,20端口是数据端口。当混入主动/被动模式的概念时,数据端口就有可能不是20了
在主动模式下,FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,
然后开放N+1号端口进行监听,并向服务器发出PORTN+1命令。
服务器接收到命令后,会用其本地的FTP数据端口(通常是20)来连接客户端指定的端口N+1,进行数据传输。
主动模式的优点:
服务端配置简单,利于服务器安全管理,服务器只需要开放21端口
主动模式的缺点:
如果客户端开启了防火墙,或客户端处于内网(NAT网关之后), 那么服务器对客户端端口发起的连接可能会失败
在被动模式下,FTP库户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时会开启N+1号端口。
然后向服务器发送PASV命令,通知服务器自己处于被动模式。
服务器收到命令后,会开放一个大于1024的端口P进行监听,然后用PORTP命令通知客户端,自己的数据端口是P。
客户端收到命令后,会通过N+1号端口连接服务器的端口P,然后在两个端口之间进行数据传输
被动模式缺点:
服务器配置管理稍显复杂,不利于安全,服务器需要开放随机高位端口以便客户端可以连接,因此大多数FTP服务软件都可以手动配置被动端口的范围
被动模式的优点:
对客户端网络环境没有要求
主动FTP:
命令连接:客户端>1023端口->服务器21端口
数据连接:客户端>1023端口<-服务器20端口
被动FTP:
命令连接:客户端>1023端口->服务器21端口
数据连接:客户端>1023端口->服务器>1023端口
安装模块 pyftpdlib
- pip3 install pyftpdlib
-
教程
http://pyftpdlib.readthedocs.io/en/latest/tutorial.html
源码
https://github.com/giampaolo/pyftpdlib
使用:
- from pyftpdlib.authorizers import DummyAuthorizer
- from pyftpdlib.handlers import FTPHandler
- from pyftpdlib.servers import FTPServer
-
- # 新建一个用户组
- authorizer = DummyAuthorizer()
- # 将用户名,密码,指定目录,权限 添加到里面
- authorizer.add_user("fan", "root", "E:/", perm="elr") # adfmw
- # 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
- authorizer.add_anonymous("E:/")
-
- handler = FTPHandler
- handler.authorizer = authorizer
- # 开启服务器
- server = FTPServer(("127.0.0.1", 21), handler)
- server.serve_forever()
-
然后将程序运行起来,接下来看一下效果,在浏览器上ftp://localhost/
- 读取权限:
-
- "e" =更改目录(CWD,CDUP命令)
-
- "l" =列表文件(LIST,NLST,STAT,MLSD,MLST,SIZE命令)
-
- "r" =从服务器检索文件(RETR命令)
-
- 写入权限:
-
- "a" =将数据追加到现有文件(APPE命令)
-
- "d" =删除文件或目录(DELE,RMD命令)
-
- "f" =重命名文件或目录(RNFR,RNTO命令)
-
- "m" =创建目录(MKD命令)
-
- "w" =将文件存储到服务器(STOR,STOU命令)
-
- "M"=更改文件模式/权限(SITE CHMOD命令)
-
- "T"=更改文件修改时间(SITE MFMT命令)
-
- #添加被动端口范围
- handler.passive_ports = range(8300, 8500)
-
demo
- from pyftpdlib.authorizers import DummyAuthorizer
- from pyftpdlib.handlers import FTPHandler
- from pyftpdlib.servers import FTPServer
-
- # 新建一个用户组
- authorizer = DummyAuthorizer()
- # 将用户名,密码,指定目录,权限 添加到里面
- authorizer.add_user("test", "1234", "E:/", perm="elradfmw") # adfmw
- # 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
- authorizer.add_anonymous("E:/")
-
- handler = FTPHandler
- handler.authorizer = authorizer
-
- #添加被动端口范围
- handler.passive_ports = range(8300, 8500)
-
- # 开启服务器
- server = FTPServer(("127.0.0.1", 2121), handler)
- server.serve_forever()
-
- -----------输出-----------------
-
[I 2018-08-06 15:37:16] >>> starting FTP server on 127.0.0.1:2121, pid=8564 <<<
[I 2018-08-06 15:37:16] concurrency model: async
[I 2018-08-06 15:37:16] masquerade (NAT) address: None
[I 2018-08-06 15:37:16] passive ports: 8300->8499
[I 2018-08-06 15:37:32] 127.0.0.1:54898-[] FTP session opened (connect)
- #-----------user.ini------
-
- [alex]
- password=123
- perm=elradfmwM
- home=D:/
-
- [egon]
- password=123456
- perm=elradfmwM
- home=D:/
-
-
- #------------ftpdemo
-
- from pyftpdlib.authorizers import DummyAuthorizer
- from pyftpdlib.handlers import FTPHandler, ThrottledDTPHandler
- from pyftpdlib.servers import FTPServer
- import configparser
- import logging
-
- IP = '127.0.0.1'
-
- PORT = '2121'
-
- # 上传速度 100kb/s
- MAX_UPLOAD = 100 * 1024
-
- # 下载速度 100kb/s
- MAX_DOWNLOAD = 100 * 1024
-
- # 最大连接数
- MAX_CONS = 100
-
- # 最多IP数
- MAX_PER_IP = 10
-
- # 被动端口范围,注意被动端口数量要比最大IP数多,否则可能出现无法连接的情况
- PASSIVE_PORTS = (8300, 8500)
-
- # 是否开启匿名访问 on|off
- ENABLE_ANONYMOUS = 'off'
-
- # 匿名用户目录
- ANONYMOUS_PATH = 'E:/DEVTOOL/'
-
-
- # 日志文件
- LOGING_NAME = 'pyftp.log'
-
- # 欢迎信息
- WELCOME_MSG = 'Welcome to my ftp'
-
- # 新建一个用户组
- authorizer = DummyAuthorizer()
-
- # 读取用户配置
- config = configparser.ConfigParser()
- config.read('user.ini')
- user_list = config.sections()
- for user in user_list:
- passwd = config[user]["password"]
- perm = config[user]["perm"]
- home_dir = config[user]["home"]
- # 将用户名,密码,指定目录,权限 添加到里面
- authorizer.add_user(user, passwd, homedir=home_dir, perm=perm)
-
- # 添加匿名用户 只需要路径
- if ENABLE_ANONYMOUS == 'on':
- authorizer.add_anonymous(ANONYMOUS_PATH)
-
- # 下载上传速度设置
- dtp_handler = ThrottledDTPHandler
- dtp_handler.read_limit = MAX_DOWNLOAD
- dtp_handler.write_limit = MAX_UPLOAD
-
- # 初始化ftp句柄
- handler = FTPHandler
- handler.authorizer = authorizer
-
- # 添加被动端口范围
- handler.passive_ports = range(PASSIVE_PORTS[0], PASSIVE_PORTS[1])
-
-
- # 欢迎信息
- handler.banner = WELCOME_MSG
-
- # 监听ip 和 端口
- server = FTPServer((IP, PORT), handler)
-
- # 最大连接数
- server.max_cons = MAX_CONS
- server.max_cons_per_ip = MAX_PER_IP
-
- # 开始服务
- print('FTP开始服务 ', (IP, PORT))
- server.serve_forever()