1、FTP服务的主动模式和被动模式:
主动模式工作过程:
- 客户端以随机非特权端口N,就是大于1024的端口,对server端21端口发起连接
- 客户端开始监听 N+1端口;
- 服务端会主动以20端口连接到客户端的N+1端口。
主动模式的优点:
服务端配置简单,利于服务器安全管理,服务器只需要开放21端口
主动模式的缺点:
如果客户端开启了防火墙,或客户端处于内网(NAT网关之后), 那么服务器对客户端端口发起的连接可能会失败
被动模式:
被动模式工作过程:
- 客户端以随机非特权端口连接服务端的21端口
- 服务端开启一个非特权端口为被动端口,并返回给客户端
- 客户端以非特权端口+1的端口主动连接服务端的被动端口
被动模式缺点:
服务器配置管理稍显复杂,不利于安全,服务器需要开放随机高位端口以便客户端可以连接,因此大多数FTP服务软件都可以手动配置被动端口的范围
被动模式的优点:
对客户端网络环境没有要求
2、搭建客户端通过【主动模式】连接服务器
服务器端配置(以下配置默认为有公网ip的服务器):
①安全组入方向映射:TCP/21端口
开别的端口会出现:ftplib.error_perm: 501 Rejected data connection to foreign address xxx.
②安全组出方向打开允许TCP所有端口
③pip3 install pyftpdlib
主动模式下,服务器端代码
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
# filepath = r"F:\videos"
filepath = "/root/workspace/zys/ftpfiles"
# 新建一个用户组
authorizer = DummyAuthorizer()
# 将用户名,密码,指定目录,权限 添加到里面
authorizer.add_user("xxx", "xxx", filepath, perm="elradfmwM")
# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
# authorizer.add_anonymous(filepath)
handler = FTPHandler
handler.authorizer = authorizer
# 开启服务器
port = 21
server = FTPServer(("0.0.0.0", port), handler)
server.serve_forever()
# 读取权限
# "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命令)
主动模式下,客户端代码(被动模式加了try-catch,可以参考)
from ftplib import FTP
import datetime
def ftpconnect(host, port, username, password):
ftp = FTP()
ftp.set_debuglevel(2)
ftp.connect(host, port)
ftp.login(username, password)
ftp.set_pasv(False) ########################主动模式
print(ftp.getwelcome())
return ftp
def downloadfile(ftp, remotepath, localpath):
# 从ftp下载文件
bufsize = 1024
fp = open(localpath, 'wb')
ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
ftp.set_debuglevel(0)
fp.close()
def uploadfile(ftp, localpath, remotepath):
# 从本地上传文件到ftp
bufsize = 1024
fp = open(localpath, 'rb')
ftp.storbinary('STOR ' + remotepath, fp, bufsize)
ftp.set_debuglevel(0)
fp.close()
if __name__ == "__main__":
ip = "xxxxxxx"
ftp = ftpconnect(ip, 21, "xxx", "xxx")
local_file = r'F:\videos\2023-02-04_01-58-09.mp4'
for i in range(2):
target_file = str(i) + 'xx.mp4'
uploadfile(ftp, local_file, target_file)
# downloadfile(ftp, "VID_20210830_103249.mp4", "11.mp4")
ftp.quit()
3、搭建客户端通过【被动模式】连接服务器
服务器端配置(以下配置默认为有公网ip的服务器):
①安全组-入方向映射:TCP/21端口,和passive_ports设定的主动模式下端口范围
②安全组出方向不用设置
③pip3 install pyftpdlib
被动模式下,服务器端代码
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
# filepath = r"F:\videos"
filepath = "/root/workspace/zys/ftpfiles"
# 新建一个用户组
authorizer = DummyAuthorizer()
# 将用户名,密码,指定目录,权限 添加到里面
authorizer.add_user("xxx", "xxx", filepath, perm="elradfmwM")
# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试
# authorizer.add_anonymous(filepath)
handler = FTPHandler
handler.authorizer = authorizer
#
#*******************添加被动端口范围, client主动模式下,不需要设置********************************
handler.passive_ports = range(38300, 38301)
#***********************************************************************
# 开启服务器
port = 21
server = FTPServer(("0.0.0.0", port), handler)
server.serve_forever()
# 读取权限
# "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命令)
被动模式下,客户端代码(重写makepasv方法)
from ftplib import FTP
import datetime
# 重写makepasv方法, *************非常重要***************
# 作用是为了数据传输时使用服务器被动模式下返回的ip和port
# 防止出现卡在:*resp* '227 Entering passive mode (192,168,1,1x,xx,xxx).'
class MyFtp(FTP):
def makepasv(self):
host, port = super(MyFtp, self).makepasv()
host = self.sock.getpeername()[0]
print(host, port)
return host, port
def ftpconnect(host, port, username, password):
connectStatus = True
ftp = MyFtp()
# ftp.set_debuglevel(2)
try:
ftp.connect(host, port)
ftp.login(username, password)
print("*********************************************************", ftp.getwelcome())
except ConnectionRefusedError as e:
print("ftp服务连接失败!")
connectStatus = False
return connectStatus, ftp
def downloadfile(ip, port, remotepath, localpath):
# ftp连接
connectStatus, ftp = ftpconnect(ip, port, "xxx", "xxx")
if not connectStatus:
pass
try:
# 从ftp下载文件
bufsize = 1024
fp = open(localpath, 'wb')
ftp.retrbinary('RETR ' + remotepath, fp.write, bufsize)
ftp.set_debuglevel(0)
fp.close()
except EOFError as e:
print("FTP服务异常,传输失败!")
except ConnectionAbortedError as e:
print("FTP服务异常,传输失败!")
except TimeoutError as e:
print("FTP服务异常,传输失败!")
except Exception as e:
print("FTP服务异常,传输失败!")
def uploadfile(ip, port, localpath, remotepath):
# ftp连接
connectStatus, ftp = ftpconnect(ip, port, "xxx", "xxx")
if not connectStatus:
pass
try:
# 从本地上传文件到ftp
bufsize = 1024
fp = open(localpath, 'rb')
ftp.storbinary('STOR ' + remotepath, fp, bufsize)
ftp.set_debuglevel(0)
fp.close()
ftp.quit()
except EOFError as e:
print("FTP服务异常,传输失败!")
except ConnectionAbortedError as e:
print("FTP服务异常,传输失败!")
except TimeoutError as e:
print("FTP服务异常,传输失败!")
except Exception as e:
print("FTP服务异常,传输失败!")
import time
if __name__ == "__main__":
ip = "xxxxx"
port = xxx # 可以是云服务的21端口,也可以是服务器21端口映射到外网的端口
local_file = r'F:\videos\2023-02-04_01-58-09.mp4'
for i in range(5):
target_file = str(i) + 'a.mp4'
try:
uploadfile(ip, port, local_file, target_file)
except Exception as e:
print(e)
time.sleep(5)
# downloadfile(ftp, "VID_20210830_103249.mp4", "11.mp4")
本次过程中出现的错误:
①主动模式:ftplib.error_perm: 501 Rejected data connection to foreign address 192.168.1.xxx.11.
②被动模式,卡在resp '227 Entering passive mode (192,168,1,1x,xx,xxx).'