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()