2020-02-22 搭建FTP服务器与客户端

1. 搭建FTP服务器

FTP(File Transfer Protocol,文件传输协议)运行在TCP协议上,使用两个端口,即数据端口和命令端口,也称控制端口。默认情况下,20是数据端口,21是命令端口。
FTP有两种传输模式:主动模式和被动模式。
(1)主动模式:客户端首先从任意的非特殊端口n(大于1023的端口,也是客户端的命令端口)连接FTP服务器的命令端口(默认是21),向服务发出命令PORT n+1,告诉服务器自己使用n+1端口作为数据端口进行数据传输,然后在n+1端口监听。服务器收到PORT n+1后向客户端返回一个'ACK',然后服务器从它自己的数据端口(20)到客户端先前指定的数据端口(n+1端口)的连接,最后客户端向服务器返回一个'ACK',过程结束。

ftp的主动模式

(2)被动模式:为了解决服务器发起到客户的连接问题,人们开发了被动FTP,或者叫作PASV,当客户端通知服务器处于被动模式时才启用。在被动模式FTP中,命令连接和数据连接都由客户端发起。当开启一个FTP连接时,客户端打开两个任意的非特权本地端口(大于1023)。第一个端口连接服务器的21端口,但与主动方式的FTP不同,客户端不会提交PORT命令并允许服务器来回连接数据端口,而是提交PASV命令。这样做的结果是服务器会开启一个任意的非特权端口,并发送PORT P命令给客户端,然后客户端发起从本地端口N+1到服务器的端口P的连接用来传送数据。

ftp的被动模式

总结:主动方式对FTP服务器的管理有利,但对客户端的管理不利。因为FTP服务器企图与客户端的高位随机端口建立连接,而这个端口很有可能被客户端的防火墙阻塞掉。被动方式对FTP客户端的管理有利,但对服务器端的管理不利。因为客户端要与服务器端建立两个连接,其中一个连到一个高位随机端口,而这个端口很有可能被服务器端的防火墙阻塞掉。
使用Python搭建一个FTP服务器需要pyftpdlib模块,安装非常简单:

pip install pyftpdlib

(1)快速搭建一个简单的FTP服务器。执行:

python -m pyftpdlib -p 21

即可在执行命令所在的目录下建立一个端口为21的供下载文件的FTP服务器,注意Linux系统需要root用户才能使用默认端口21,windows系统中目录文件名可能是乱码,原因是pyftpdlib内部使用utf8,而windows使用gbk,参照下面的步骤可解决windows系统的乱码问题。
首先,找到pyftpdlib源文件所在的目录。

[root@localhost ~]# python3
Python 3.6.5 (default, Jun 29 2019, 14:06:04) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-36)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyftpdlib
>>> pyftpdlib.__path__
['/usr/local/python3.6.5/lib/python3.6/site-packages/pyftpdlib']

其次,在目录pyftpdlib源文件所在的目录找到文件filesystems.py和handlers.py,先备份。

[root@localhost ~]# cd /usr/local/python3.6.5/lib/python3.6/site-packages/pyftpdlib
[root@localhost pyftpdlib]# ls
authorizers.py  filesystems.py  __init__.py  log.py       prefork.py   servers.py
_compat.py      handlers.py     ioloop.py    __main__.py  __pycache__  test

再次,打开filesystems.py,找到

yield line.encode('utf8, self.cmd_channel.unicode_errors)

共有两处,修改'utf8'为'gbk',保存退出。
打开handlers.py,找到

return bytes.decode('utf8', self.unicode_errors)

修改utf8为gbk,保存退出。
最后,验证乱码已解决。
(2)搭建一个具有访问权限,可配置相关信息的FTP服务器(ftpserver.py)。

from pyftpdlib.authorizers import DummyAuthorizer
from pyftpdlib.handlers import FTPHandler,ThrottledDTPHandler
from pyftpdlib.servers import FTPServer
from pyftpdlib.log import LogFormatter
import logging


#记录日志,默认情况下日志仅输出到屏幕(终端),这里既输出到屏幕又输出到文件,方便日志查看
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ch = logging.StreamHandler()
fh = logging.FileHandler(filename='myftpserver.log',encoding='utf-8')   #默认的方式是追加到文件
ch.setFormatter(LogFormatter())
fh.setFormatter(LogFormatter())
logger.addHandler(ch)   #将日志输出至屏幕
logger.addHandler(fh)   #将日志输出至文件


# 实例化虚拟用户,这是FTP验证首要条件
authorizer = DummyAuthorizer()
# 添加用户权限和路径,括号内的参数是(用户名、密码、用户目录、权限),可以为不同的用户添加不同的目录和权限
authorizer.add_user("user", "12345", "/ftp", perm="elradfmw")
# 添加匿名用户,只需要路径
authorizer.add_anonymous("/ftp")

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

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

# 下载上传速度
dtp_handler = ThrottledDTPHandler
dtp_handler.read_limit = 300 * 1024 #300kb/s
dtp_handler.write_limit  300 * 1024 #300kb/s
handler.dtp_handler = dtp_handler

# 监听ip和端口,linux里需要root用户
server = FTPServer(("0.0.0.0", 21), handler)

# 最大连接数
server.max_cons = 150
server.max_cons_per_ip = 15

# 开始服务,自带日志打印信息
server.serve_forever()

执行python ftpserver.py

[root@localhost ~]# python3 ftpserver.py 
[I 2020-02-22 17:03:23] concurrency model: async
[I 2020-02-22 17:03:23] masquerade (NAT) address: None
[I 2020-02-22 17:03:23] passive ports: 2000->2332
[I 2020-02-22 17:03:23] >>> starting FTP server on 0.0.0.0:21, pid=7716 <<<

同时该目录下也会生成一个myftpserver.log文件,文件内容与屏幕上的信息一致。
下面我们登录该FTP并列出目录进行测试:

C:\Users\willi>ftp 192.168.9.43
连接到 192.168.9.43。
220 pyftpdlib 1.5.6 ready.
530 Log in with USER and PASS first.
用户(192.168.9.43:(none)): user
331 Username ok, send password.
密码:
230 Login successful.
ftp> dir
200 Active data connection established.
125 Data connection already open. Transfer starting.
226 Transfer complete.
ftp> dir
200 Active data connection established.
125 Data connection already open. Transfer starting.
drwxr-xr-x   2 root     root            6 Feb 22 09:19 test
226 Transfer complete.
ftp: 收到 64 字节,用时 0.00秒 32.00千字节/秒。
ftp>

对应服务器的打印信息:

[root@localhost ~]# python3 ftpserver.py 
[I 2020-02-22 17:03:23] concurrency model: async
[I 2020-02-22 17:03:23] masquerade (NAT) address: None
[I 2020-02-22 17:03:23] passive ports: 2000->2332
[I 2020-02-22 17:03:23] >>> starting FTP server on 0.0.0.0:21, pid=7716 <<<
[I 2020-02-22 17:18:40] 192.168.9.206:8439-[] FTP session opened (connect)
[I 2020-02-22 17:18:48] 192.168.9.206:8439-[user] USER 'user' logged in.
[I 2020-02-22 17:24:25] 192.168.9.206:8439-[user] Control connection timed out.
[I 2020-02-22 17:24:25] 192.168.9.206:8439-[user] FTP session closed (disconnect).

至此,一个FTP服务器已经搭建完成,可以修改ftpserver.py来满足自己的需求。
用户权限的代码及说明:

image.png
image.png

2. 编写FTP客户端程序

在实际应用中可能经常访问FTP服务器来上传或下载文件,Python也可以替我们做这些。

# -*- coding: utf-8 -*-
# !/usr/local/bin/python
# Time: 2020/2/22 22:22:22
# Description:
# File Name: ftpclient.py

from ftplib import FTP
#登录FTP
ftp = FTP('localhost', user='user', passwd='12345')
#设置编码方式,由于在windows系统,设置编码为gbk
ftp.encoding = 'gbk'
# 切换目录
ftp.cwd('test')
#列出文件夹的内容
ftp.retrlines('LIST')   # ftp.dir()
#下载文件 note.txt
ftp.retrbinary('RETR note.txt', open('note.txt', 'wb').write)
#上传文件 ftpserver.py
ftp.storbinary('STOR ftpserver.py', open('ftpserver.py', 'rb'))
#查看目录下的文件详情
for f in ftp.mlsd(path='/test'):
    print(f)

运行结果如下:

[root@localhost ~]# python3 ftpclient.py 
-rw-r--r--   1 root     root            0 Feb 22 16:58 note.txt
('note.txt', {'modify': '20200222165853', 'perm': 'radfw', 'size': '0', 'type': 'file', 'unique': 'fd00gf155'})
('ftpserver.py', {'modify': '20200222165941', 'perm': 'radfw', 'size': '1681', 'type': 'file', 'unique': 'fd00gf156'})

注意:操作前需要先进入/ftp目录创建对应的目录和文件。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容