1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > Python实现FTP服务器和客户端

Python实现FTP服务器和客户端

时间:2022-09-25 11:28:17

相关推荐

Python实现FTP服务器和客户端

基础知识

FTP只通过TCP连接,FTP不同于其他服务的是它使用了两个端口,一个数据端口和一个命令端口(或称为控制端口)。

通常21端口是命令端口,20端口是数据端口。当混入主动/被动模式的概念时,数据端口就有可能不是20了

FTP主动模式

在主动模式下,FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,

然后开放N+1号端口进行监听,并向服务器发出PORTN+1命令。

服务器接收到命令后,会用其本地的FTP数据端口(通常是20)来连接客户端指定的端口N+1,进行数据传输。

FTP服务器命令(21)端口接受客户端任意端口(客户端初始连接)FTP服务器命令(21)端口到客户端端口(>1023)(服务器响应客户端命令)FTP服务器数据(20)端口到客户端端口(>1023)(服务器初始化数据连接到客户端数据端口)FTP服务器数据(20)端口接受客户端端口(>1023)(客户端发送ACK包到服务器的数据端口)

主动模式的优点:

服务端配置简单,利于服务器安全管理,服务器只需要开放21端口

主动模式的缺点:

如果客户端开启了防火墙,或客户端处于内网(NAT网关之后), 那么服务器对客户端端口发起的连接可能会失败

FTP被动模式

在被动模式下,FTP库户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时会开启N+1号端口。

然后向服务器发送PASV命令,通知服务器自己处于被动模式。

服务器收到命令后,会开放一个大于1024的端口P进行监听,然后用PORTP命令通知客户端,自己的数据端口是P。

客户端收到命令后,会通过N+1号端口连接服务器的端口P,然后在两个端口之间进行数据传输

FTP服务器命令(21)端口接受客户端任意端口(客户端初始连接)FTP服务器命令(21)端口到客户端端口(>1023)(服务器响应客户端命令)FTP服务器数据端口(>1023)接受客户端端口(>1023)(客户端初始化数据连接到服务器指定的任意端口)FTP服务器数据端口(>1023)到客户端端口(>1023)(服务器发送ACK响应和数据到客户端的数据端口)

被动模式缺点:

服务器配置管理稍显复杂,不利于安全,服务器需要开放随机高位端口以便客户端可以连接,因此大多数FTP服务软件都可以手动配置被动端口的范围

被动模式的优点:

对客户端网络环境没有要求

使用python来实现FTP服务

安装模块 pyftpdlib

pip3 install pyftpdlib

http://pyftpdlib.readthedocs.io/en/latest/tutorial.html教程

源码

/giampaolo/pyftpdlib

使用:

from pyftpdlib.authorizers import DummyAuthorizerfrom pyftpdlib.handlers import FTPHandlerfrom pyftpdlib.servers import FTPServer# 新建一个用户组authorizer = DummyAuthorizer()# 将用户名,密码,指定目录,权限 添加到里面authorizer.add_user("fan", "root", "E:/", perm="elr") # adfmw# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试authorizer.add_anonymous("E:/")handler = FTPHandlerhandler.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)

from pyftpdlib.authorizers import DummyAuthorizerfrom pyftpdlib.handlers import FTPHandlerfrom pyftpdlib.servers import FTPServer# 新建一个用户组authorizer = DummyAuthorizer()# 将用户名,密码,指定目录,权限 添加到里面authorizer.add_user("test", "1234", "E:/", perm="elradfmw") # adfmw# 这个是添加匿名用户,任何人都可以访问,如果去掉的话,需要输入用户名和密码,可以自己尝试authorizer.add_anonymous("E:/")handler = FTPHandlerhandler.authorizer = authorizer#添加被动端口范围handler.passive_ports = range(8300, 8500)# 开启服务器server = FTPServer(("127.0.0.1", 2121), handler)server.serve_forever()-----------输出-----------------[I -08-06 15:37:16] >>> starting FTP server on 127.0.0.1:2121, pid=8564 <<<[I -08-06 15:37:16] concurrency model: async[I -08-06 15:37:16] masquerade (NAT) address: None[I -08-06 15:37:16] passive ports: 8300->8499[I -08-06 15:37:32] 127.0.0.1:54898-[] FTP session opened (connect)

读取用户列表,建立FTP

#-----------user.ini------[alex]password=123perm=elradfmwMhome=D:/[egon]password=123456perm=elradfmwMhome=D:/#------------ftpdemofrom pyftpdlib.authorizers import DummyAuthorizerfrom pyftpdlib.handlers import FTPHandler, ThrottledDTPHandlerfrom pyftpdlib.servers import FTPServerimport configparserimport loggingIP = '127.0.0.1'PORT = '2121'# 上传速度 100kb/sMAX_UPLOAD = 100 * 1024# 下载速度 100kb/sMAX_DOWNLOAD = 100 * 1024# 最大连接数MAX_CONS = 100# 最多IP数MAX_PER_IP = 10# 被动端口范围,注意被动端口数量要比最大IP数多,否则可能出现无法连接的情况PASSIVE_PORTS = (8300, 8500)# 是否开启匿名访问 on|offENABLE_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 = ThrottledDTPHandlerdtp_handler.read_limit = MAX_DOWNLOADdtp_handler.write_limit = MAX_UPLOAD# 初始化ftp句柄handler = FTPHandlerhandler.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_CONSserver.max_cons_per_ip = MAX_PER_IP# 开始服务print('FTP开始服务 ', (IP, PORT))server.serve_forever()

基于twisted

#!/usr/bin/env python # -*- coding: utf-8 -*- """a very simple ftp server by twisted """ __author__ = "Bobning(nb5550606@)" import sys, os sys.path.append('../../..') os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' from twisted.protocols import ftp from twisted.cred import portal, checkers, credentials, error as credError from twisted.internet import reactor, defer from zope.interface import implements from twisted.python import filepath from django.contrib.auth import authenticate from django.contrib.auth.models import User class UserChecker: implements(checkers.ICredentialsChecker) credentialInterfaces = (credentials.IUsernamePassword,) def __init__(self): "passwords: a dict-like object mapping usernames to passwords" def requestAvatarId(self, credentials): user = authenticate(username=credentials.username, password=credentials.password) if user is not None: if user.is_active: return defer.succeed(credentials.username) else: return defer.fail(credError.UnauthorizedLogin("access denied")) else: return defer.fail(credError.UnauthorizedLogin("No such user")) class MyFTPRealm: implements(portal.IRealm) def __init__(self, anonymousRoot): self.anonymousRoot = filepath.FilePath(anonymousRoot) def requestAvatar(self, avatarId, mind, *interfaces): for iface in interfaces: if iface is ftp.IFTPShell: if avatarId is checkers.ANONYMOUS: avatar = ftp.FTPAnonymousShell(self.anonymousRoot) else: try: user = User.objects.get(username=avatarId) ftpdir = user.ftp.all()[0].ftpdir except: raise "没有该用户" avatar = ftp.FTPShell(filepath.FilePath(ftpdir.encode("utf-8"))) return ftp.IFTPShell, avatar, getattr(avatar, "logout", lambda:None) raise NotImplementedError("only IFTPShell interface is supported by this realm") class MyFtpServer(ftp.FTP): def __init__(self, *args, **kw): super(ftp.FTP, self).__init__(*args, **kw) def dataReceived(self, data): self.transport.write(data) if __name__ == "__main__": p = portal.Portal(MyFTPRealm("")) p.registerChecker(UserChecker()) factory = ftp.FTPFactory(MyFtpServer()) factory.portal = p reactor.listenTCP(2121, factory) reactor.run()

客户端实现

import ftplibimport sys#获取服务器的ip地址(如192.168.1.107),使用sys.argv可以从命令行参数里面获取if len(sys.argv) < 2:tmp = input("please input server address:")sys.argv.append(tmp)server_address = sys.argv[1]#创建FTP实例,并显示欢迎界面ftp = ftplib.FTP(server_address)print(ftp.getwelcome())#登录,输入服务器里添加过的用户名和口令ftp.login('user', 'pass')#文件上传def upload(fname):fd = open(fname, 'rb')new_name = input("input new name:")#以二进制的形式上传ftp.storbinary("STOR %s" % new_name, fd)fd.close()print("upload finished")#文件下载def download(fname):#构建文件的存储路径,这里用的是D盘,可以自行设置new_path = "D:\\FTPdownload\\" + fnamefd = open(new_path, 'wb')#以二进制形式下载,注意第二个参数是fd.write,上传时是fdftp.retrbinary("RETR %s" % fname, fd.write)fd.close()print("download finished")def main():#选择操作,上传、下载、退出op = input("what do you want?(u/d/q)")if op == "u":#输入文件完整路径,必要时可以用绝对路径fname = input("input the file of path:")upload(fname)elif op == "d":fname = input("input the file name:")download(fname)else:print("quit now!")ftp.quit()if __name__ == '__main__':main()

附带一些ftplib库的相关操作:

ftp.cwd(pathname) # 设置FTP当前操作的路径ftp.dir() # 显示目录下所有目录的信息ftp.nlst() # 获取目录下的文件ftp.mkd(pathname) # 新建远程目录ftp.rmd(dirname) # 删除远程目录ftp.pwd() # 返回当前所在位置ftp.delete(filename) # 删除远程文件ftp.rename(old_name, new_name) #将fromname改为tonameftp.storbinary('STOR filename.txt',file_handel,[bufsize]) # 上传目标文件,最后一个参数可以不填ftp。retrbinary('RETR filename.txt',file_handel,[bufsize]) # 下载FTP文件

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。