程序员运维文档简友广场

python通过pyftpdlib实现FTP服务端和客户端

2021-01-11  本文已影响0人  微笑沉默

1. 使用pyftpdlib模块搭建FTP服务器

在我上一篇文章里面,详细的介绍了在linux操作系统下面搭建vsftpd服务,那么我们如何通过python实现一个FTP服务器呢?本文会有一个详细的介绍,搭建FTP服务器需要用到python的pyftpdlib,这是一个第三方模块需要使用pip进行安装,具体安装步骤如下:

pip3 install -i https://mirrors.aliyun.com/pypi/simple pyftpdlib
[root@zhanghao ~]# python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyftpdlib
>>> pyftpdlib.__doc__
'\npyftpdlib: RFC-959 asynchronous FTP server.\n\npyftpdlib implements a fully functioning asynchronous FTP server as\ndefined in RFC-959.  A hierarchy of classes outlined below implement\nthe backend functionality for the FTPd:\n\n    [pyftpdlib.ftpservers.FTPServer]\n      accepts connections and dispatches them to a handler\n\n    [pyftpdlib.handlers.FTPHandler]\n      a class representing the server-protocol-interpreter\n      (server-PI, see RFC-959). Each time a new connection occurs\n      FTPServer will create a new FTPHandler instance to handle the\n      current PI session.\n\n    [pyftpdlib.handlers.ActiveDTP]\n    [pyftpdlib.handlers.PassiveDTP]\n      base classes for active/passive-DTP backends.\n\n    [pyftpdlib.handlers.DTPHandler]\n      this class handles processing of data transfer operations (server-DTP,\n      see RFC-959).\n\n    [pyftpdlib.authorizers.DummyAuthorizer]\n      an "authorizer" is a class handling FTPd authentications and\n      permissions. It is used inside FTPHandler class to verify user\n      passwords, to get user\'s home directory and to get permissions\n      when a filesystem read/write occurs. "DummyAuthorizer" is the\n      base authorizer class providing a platform independent interface\n      for managing virtual users.\n\n    [pyftpdlib.filesystems.AbstractedFS]\n      class used to interact with the file system, providing a high level,\n      cross-platform interface compatible with both Windows and UNIX style\n      filesystems.\n\nUsage example:\n\n>>> from pyftpdlib.authorizers import DummyAuthorizer\n>>> from pyftpdlib.handlers import FTPHandler\n>>> from pyftpdlib.servers import FTPServer\n>>>\n>>> authorizer = DummyAuthorizer()\n>>> authorizer.add_user("user", "12345", "/home/giampaolo", perm="elradfmwMT")\n>>> authorizer.add_anonymous("/home/nobody")\n>>>\n>>> handler = FTPHandler\n>>> handler.authorizer = authorizer\n>>>\n>>> server = FTPServer(("127.0.0.1", 21), handler)\n>>> server.serve_forever()\n[I 13-02-19 10:55:42] >>> starting FTP server on 127.0.0.1:21 <<<\n[I 13-02-19 10:55:42] poller: <class \'pyftpdlib.ioloop.Epoll\'>\n[I 13-02-19 10:55:42] masquerade (NAT) address: None\n[I 13-02-19 10:55:42] passive ports: None\n[I 13-02-19 10:55:42] use sendfile(2): True\n[I 13-02-19 10:55:45] 127.0.0.1:34178-[] FTP session opened (connect)\n[I 13-02-19 10:55:48] 127.0.0.1:34178-[user] USER \'user\' logged in.\n[I 13-02-19 10:56:27] 127.0.0.1:34179-[user] RETR /home/giampaolo/.vimrc\n                      completed=1 bytes=1700 seconds=0.001\n[I 13-02-19 10:56:39] 127.0.0.1:34179-[user] FTP session closed (disconnect).\n'

pyftpdlib实现了一个功能完整的异步FTP服务,在rfc-959中定义的。

pyftpdlib.ftpservers.FTPServer : 接收客户端连接,然后分发给对应的程序
pyftpdlib.handlers.FTPHandler : 一个表示服务器协议解释器的类,每次出现一个新的连接FTPServer将创建一个新的FTPHandler实例来处理当前PI会话。
pyftpdlib.handlers.ActiveDIPpyftpdlib.handlers.PassiveDIP : 主动模式和被动模式的基础类
pyftpdlib.handlers.DTPHandler : 这个类处理的是数据传输进程
pyftpdlib.authorizers.DummyAuthorizer : 处理FTPd的认证和权限的处理类,用于处理用户密码,获取用户的家目录和权限,DummyAuthorizer 是基础的认证模块的类,提供平台无关接口的基础授权程序类
用于管理虚拟用户。
pyftpdlib.filesystems.AbstractedFS : 类用于与文件系统交互,提供了一个高级的,跨平台接口兼容Windows和UNIX风格文件系统。

1.1 实现脚本

1.1.1 加载模块
from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.log import LogFormatter
1.1.2 添加一个FTP的handler
def ftpServer(username, password, directory):

    '''
    启动一个ftp的server进程
    :param username: 需要添加用户的用户名
    :param password: 用户的密码
    :param directory: 用户的家目录,也就是ftp的数据目录
    :return:
    '''

    # 添加虚拟用户,验证用户需要用户名、密码、家目录、权限
    author = DummyAuthorizer()
    author.add_user(username, password, directory, perm="elradfmw")

    # 初始化ftp句柄
    handler = FTPHandler
    handler.authorizer = author

    # 添加被动端口范围
    handler.passive_ports = range(2000, 2400)

    # 启动server,本地执行,监听21号端口
    server = FTPServer(('0.0.0.0', 32), handler)
    server.serve_forever()

username = "zhanghao"
password = "aixocm"
directory = "/data/zhanghao"
ftpServer(username, password, directory)

运行报错如下:对于脚本需要添加异常处理,捕获到目录不存在,需要创建目录

[root@zhanghao python-learning]# python3 ftptest.py 
Traceback (most recent call last):
  File "ftptest.py", line 37, in <module>
    ftpServer(username, password, directory)
  File "ftptest.py", line 21, in ftpServer
    author.add_user(username, password, directory, perm="elradfmw")
  File "/usr/local/lib/python3.6/site-packages/pyftpdlib/authorizers.py", line 107, in add_user
    raise ValueError('no such directory: %r' % homedir)
ValueError: no such directory: '/data/zhanghao'

运行报错如下:如果端口被占用,需要捕获报错,然后将输出:"21号端口被占用"

[root@zhanghao python-learning]# python3 ftptest.py 
Traceback (most recent call last):
  File "ftptest.py", line 37, in <module>
    ftpServer(username, password, directory)
  File "ftptest.py", line 31, in ftpServer
    server = FTPServer(('0.0.0.0', 21), handler)
  File "/usr/local/lib/python3.6/site-packages/pyftpdlib/servers.py", line 118, in __init__
    self.bind_af_unspecified(address_or_socket)
  File "/usr/local/lib/python3.6/site-packages/pyftpdlib/ioloop.py", line 1018, in bind_af_unspecified
    raise socket.error(err)
OSError: [Errno 98] Address already in use

完善脚本之后

#!/usr/bin/env python3
#coding:utf-8

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.log import LogFormatter
import os

def ftpServer(username, password, directory):

    '''
    启动一个ftp的server进程
    :param username: 需要添加用户的用户名
    :param password: 用户的密码
    :param directory: 用户的家目录,也就是ftp的数据目录
    :return:
    '''

    # 如果目录不存在,就创建目录
    if not os.path.exists(directory):
        os.mkdir(directory)
    
    try:
        # 添加虚拟用户,验证用户需要用户名、密码、家目录、权限
        author = DummyAuthorizer()
        author.add_user(username, password, directory, perm="elradfmw")
    except OSError as e:
        if "Address already in use" in e:
            print("21号端口被占用")

    # 初始化ftp句柄
    handler = FTPHandler
    handler.authorizer = author

    # 添加被动端口范围
    handler.passive_ports = range(2000, 2400)

    # 启动server,本地执行,监听21号端口
    server = FTPServer(('0.0.0.0', 21), handler)
    server.serve_forever()

username = "zhanghao"
password = "aixocm"
directory = "/data/zhanghao"
ftpServer(username, password, directory)

正常输出

[root@zhanghao python-learning]# python3 ftptest.py 
[I 2021-01-09 05:40:02] concurrency model: async
[I 2021-01-09 05:40:02] masquerade (NAT) address: None
[I 2021-01-09 05:40:02] passive ports: 2000->2399
[I 2021-01-09 05:40:02] >>> starting FTP server on 0.0.0.0:32, pid=4772 <<<

客户端连接

[root@zhserver zhanghao]# ftp 192.168.0.101
Connected to 192.168.0.101 (192.168.0.101).
220 pyftpdlib 1.5.6 ready.
Name (192.168.0.101:root): zhanghao
331 Username ok, send password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
1.1.3 FTP服务器资源限制
 from pyftpdlib.handlers import ThrottledDTPHandler
transport_handler = ThrottledDTPHandler
transport_handler.read_limit = 200 * 1024
transport_handler.write_limit = 100 * 1024
handler.dtp_handler = transport_handler
1.1.4 添加输出log

脚本中添加log,输出到屏幕和日志文件,日志文件名称为ftp.log

from pyftpdlib.log import LogFormatter
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)
    
stream = logging.StreamHandler()
log_file = logging.FileHandler(filename="ftp.log", encoding="utf-8")
    
stream.setFormatter(LogFormatter())
log_file.setFormatter(LogFormatter())
    
logger.addHandler(stream)
logger.addHandler(log_file)
1.1.5 用户权限
参数 含义
e 读权限,改变文件目录
l 读权限,可以列出所有的文件
r 读权限,可以从服务器下载文件
a 写权限,可以上传文件
d 写权限,删除文件
f 写权限,文件重命名
m 写权限,创建文件
w 写权限
M 文件传输的模式

2. ftplib客户端实现

from ftplib import FTP
def ftpClient(host_ip, username, password):
    '''
    ftp客户端程序
    :param host_ip:ftp server的IP地址
    :param username: 连接server端的用户名
    :param password: 用户密码
    :return:
    '''
    ftp_client = FTP(host=host_ip, user=username, passwd=password)
    ftp_client.encoding = 'gbk'
    
    # 执行客户端的操作
    ftp_client.cwd()
上一篇下一篇

猜你喜欢

热点阅读