python 实现简单的FTP程序


Posted in Python onDecember 27, 2019

FTP即文件传输协议;它基于客户机-服务器模型体系结构,应用广泛。它有两个通道:一个命令通道和一个数据通道。命令通道用于控制通信,数据通道用于文件的实际传输。使用FTP可以做很多事情,比如移动、下载、复制文件等。

一、开发环境

server端:centos 7  python-3.6.2

客户端:Windows 7 python-3.6.2 pycharm-2018

程序目的:1、学习使用socketserver实现并发处理多个客户端。

           2、了解使用struct解决TCP粘包。

二、程序设计

(本人菜鸟一枚,对于开发规范,接口设计完全不懂,完全是随心所欲,自娱自乐。写博客主要是记录自己学习的点点滴滴,如有不足之处还请见谅。)

1、server端

1.1 目录结构如下:

python 实现简单的FTP程序

 1.2 目录简介:

FTP_SERVER:程序主目录

app:程序主逻辑目录,目录下有四个模块:

          FTPserver.py:FTP  Server端启动入口。

          login.py:认证注册模块,用于处理用户注册,登录认证。

          dataAnalysis.py:命令解析模块,负责解析,执行客户端命令。

          FileOpertion.py:负责文件读,写。数据发送,数据接收。

db:存放user_pwd.db文件,用于存放用户信息(用户名,密码,FTP目录总空间,已使用空间等)

lib:存放公共数据。

1.3 模块中类的继承关系

python 实现简单的FTP程序

1.4 执行流程

1.4.1 程序启动文件FTPserver.py,程序启动后进入监听状态。核心代码如下:

class MyFtpServer(socketserver.BaseRequestHandler):
 
 def handle(self): # 重写handle方法,处理socket请求
 print(f"连接来自{self.client_address}的客户端")
 commom_obj = Commom()
 data_analy = DataAnalysis()
 login_obj = Login()
 while 1:
 # 执行用户选项:1、登陆系统 2、注册账号。并返回一个结果
 status_id = login_obj.run_client_choice(self.request, commom_obj)
 if status_id == "01": # 登陆成功
 if not self.run_ftp_server(data_analy,commom_obj): # 执行ftpserver主功能
  break
 elif int(status_id) == -1: # client断开连接了
 break
 print(f"客户端{self.client_address}断开了连接")

 def run_ftp_server(self,data_analy,commom_obj):
 """"
 登陆成功后,接收客户端发来的命令,并进行处理
 :param data_analy:负责解析,执行客户端命令的对象
 :param commom_obj:程序执行时所需的数据对象
 :return 返回false代表客户端断开连接了
 """
 while True:
 try:
 cmd_len_pack = self.request.recv(4) 
 cmd_len = struct.unpack('i',cmd_len_pack)[0] # 获取命令长度,防止粘包 
 except Exception:
 break
 recv_data = self.request.recv(cmd_len).decode('utf-8') # 接收客户端数据
 if recv_data.upper() == "Q": # 客户端提出断开连接了
 break
 # 解析,处理客户端的命令
 data_analy.syntax_analysis(recv_data, self.request, commom_obj)
 return False
if __name__ == '__main__':
 print('运行FTP服务')
 ip_port = ('192.168.10.10',9000)
 # 创建并发服务端对象
 server = socketserver.ThreadingTCPServer(ip_port, MyFtpServer)
 # 开启服务
 server.serve_forever()

 1.4.2 服务端进入监听状态后,客户端发起连接请求,服务端接收连接请求后会等待客户单发来状态码,1表示请求登录FTP服务器,2表示客户端要注册用户,注册用户需要服务端手动反馈状态码1才可注册。处理用户登录,注册模块login.py核心代码如下:

class Login(FileOperation):
 """
 登陆注册类。主要负责用户的登陆认证,和用户注册。
 """
 def run_client_choice(self,socket_obj,commom):
 """
 获取客户端的请求,1是登陆,2是注册用户
 :param socket_obj: socket对象
 :param commom: ftpserver运行时所需要的数据对象
 :return:
 """
 recv_choice = socket_obj.recv(1).decode("utf-8") # 获取用户选项:1是登陆,2是注册用户
 if recv_choice == "1": # client请求登陆
 return self.login_authen(socket_obj,commom)
 elif recv_choice == "2": # client请求注册账号
 return self.register_user(socket_obj,commom)
 else:
 return -1 # client断开连接了
 # 用户登陆认证
 def login_authen(self,socket_obj,commom):
 """
 客户端登陆认证
 :param socket_obj: socket对象
 :param commom: ftpserver运行时需要的数据对象
 :return:返回1代表登陆成功
 """
 # 接收client发来的用户名,密码
 recv_userPwd = self.recv_data(socket_obj).decode("utf-8").split("|") 
 # 效验用户名密码
 check_ret = self.check_user_pwd(recv_userPwd, socket_obj,commom)
 if check_ret: # 用户名密码正确
 self.check_user_home_dir(commom,recv_userPwd[0]) # 检测用户家目录
 return commom.status_info["login_success"]
 else:
 return commom.status_info["login_fail"]
 ...
 # 注册用户
 def register_user(self,socket_obj,commom):
 """
 :param socket_obj:
 :param commom:
 :return: 返回是否允许注册的结果,1允许客户端注册,2拒绝客户端注册
 """
 while True:
 choice_id = input("请输入回应码:1是允许注册,2是不允许注册:")
 if choice_id.isdigit() and 3 > int(choice_id) > 0:
 socket_obj.send(choice_id.encode("utf-8")) # 发通知告知客户端,处理结果
 if choice_id == "1": # 注册用户
  return self.client_register(socket_obj, commom)
 return choice_id
 else:
 print("您输入的信息有误,请重新输入。")
 ...

 1.4.3 客户端登录成功后,服务端会等待接收客户端发来的命令,命令的解析,执行由dataAnalysis.py模块执行,核心代码如下:

class DataAnalysis(FileOperation):
 """
 数据分析处理类,主要负责解析client发送过来的指令。
 """
 def syntax_analysis(self,recv_data, socket_obj, commom):
 """
 负责解析客户端传来的数据。
 :param recv_data:接收到的客户端用户数据
 :param socket_obj:socket对象
 :param commom:数据对象
 :return:
 """
 clientData = recv_data.split(" ")
 if hasattr(self,clientData[0]): # 判断对象方法是否存在
 get_fun = getattr(self,clientData[0])#获取对象方法
 get_fun(clientData,socket_obj,commom) # 运行对象方法
 else:
 pass
 ...

执行客户端命令后,继续等待接收客户端发来的命令,如此循环...。

2、客户端

2.1 目录结构如下:

python 实现简单的FTP程序

2.2 目录简介:

client:程序主目录。

bin:程序入口,程序启动文件main.py用于建立socket连接,然后调用FTPclient.py模块下的run_ftp_client方法运行程序。

app:程序主逻辑,目录下有四个模块如下:

          FTPclient.py:FTP客户端,根据用户选项,执行用户指令。

          login.py:认证注册模块,用于处理用户注册,登录认证。

          dataAnalysis.py:命令解析模块,解析用户输入的命令,发给服务端获取结果。

          FileOpertion.py:负责文件读,写。

lib:存放公共数据,有两个文件:

       commom.py:主要存放的是公共变量。

       help.txt:存放的是帮助文档,当用户执行help命令时会调用该文件。

2.3 模块中类的继承关系

python 实现简单的FTP程序

2.4 执行流程

2.4.1 程序入口main.py,启动后会与FTP服务端建立连接,与服务端连接成功后会调用FTPclient.py模块下的run_ftp_client方法,执行用户功能。核心代码如下:

socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect(("192.168.10.10",9000))

client_obj = Client()
client_obj.run_ftp_client(socket_obj) # 接收用户输入的选项,执行对应的功能

2.4.2 FTPclient.py模块下的run_ftp_client方法会打印菜单,并等待用户输入选项,执行相应功能,核心代码如下:

class Client(Login,DataAnalysis):
 def run_ftp_client(self,socket_obj):
 """
 运行用户输入的选项:1、是登陆 2、是注册账号
 :return:
 """
 while True:
 self.login_menu() # 打印系统菜单
 choice_id = self.get_user_choice() # 获取用户输入的选项
 if choice_id:
 if self.run_user_choice(choice_id,socket_obj):
  break
 else:
 print("您输入的有误")
 def get_user_choice(self):
 """
 获取用户输入的选项
 :return:
 """
 choice_id = input("请输入选项:")
 if choice_id.isdigit() and 4 > int(choice_id) > 0 or choice_id.upper() == "Q":
 return choice_id
 return False
 def run_user_choice(self,choice_id,socket_obj):
 if choice_id == "1": # 登陆系统
 socket_obj.send(choice_id.encode("utf-8")) # 发通知告知服务器准备登陆
 if self.run_login(socket_obj) == True: # 执行登陆
 return True
 elif choice_id == "2": # 注册用户
 socket_obj.send(choice_id.encode("utf-8")) # 请求服务器,注册用户
 self.register_user(socket_obj) # 执行注册
 elif choice_id.upper() == "Q": # 退出程序
 socket_obj.send(choice_id.encode("utf-8")) # 通知服务器,准备退出程序
 socket_obj.close()
 print("程序正常退出")
 return True
 def run_login(self,socket_obj,):
 """
 运行登陆认证模块,如果登陆成功执行程序主逻辑,否则重新登陆。
 :param socket_obj:
 :return:
 """
 if self.login_authention(socket_obj):
 while True:
 send_data = input(">>>").strip(" ") # 获取发送数据(用户执行的命令)
 if send_data.upper() == "Q": # 正常退出程序
  socket_obj.send(send_data.encode("utf-8")) # 通知服务区断开连接
  socket_obj.close()
  print("程序正常退出")
  return True
 if self.syntax_analysis(send_data, socket_obj): # 解析用户数据并处理数据
  print("异常退出")
  return True
 return False
 def login_menu(self):
 print("-"*41)
 print(" 欢迎登陆迷你FTPv1.0")
 print("-"*41)
 print("1、登陆系统")
 print("2、用户注册")
 print("Q、退出程序")

2.4.3 login.py模块主要用于处理注册和登录的功能,核心代码如下:

class Login(Commom):
 def login_authention(self,socket_obj):
 """
 登陆认证
 :param socket_obj:socket 对象
 :return:
 """
 user_pwd = self.get_user_pwd() # 获取用户名密码
 self.send_data(socket_obj,user_pwd) # 将用户名和密码发给服务器
 recv_status = socket_obj.recv(2).decode("utf-8") # 等待接收状态码
 print(self.status_info[recv_status]) # 打印状态码对应的结果
 if self.status_info[recv_status] == '登录成功':
 return True
 return False
 ...
 def register_user(self,socket_obj):
 """
 等待服务端反馈是否允许注册用户。
 :param socket_obj:
 :return:
 """
 print("请等待服务端回应.....")
 recv_status = socket_obj.recv(1).decode("utf-8")
 if recv_status == "1": # 服务端同意申请账号
 user_pwd = self.get_regist_user_pwd() # 获取注册用户名和密码
 if user_pwd:
 self.send_data(socket_obj,user_pwd)
 result = socket_obj.recv(2).decode("utf-8")
 print(self.status_info[result])
 else:
 print("用户名密码有误")
 else: # 客户端拒绝申请账号的请求
 print("服务端拒绝了您申请账号的请求,请与管理员取得联系。")
 return False
 ...

2.4.4 用户登录成功后,会等待接收用户输入命令,由dataAnalysis.py模块负责解析用户输入的命令,并将命令发给FTP服务器,然后接收服务器的反馈。核心代码如下:

class DataAnalysis(FileOperation):
 def syntax_analysis(self,cmd,socket_obj):
 """
 解析用户输入的命令。
 :param cmd:用户执行的命令,如:put 上传的文件
 :param socket_obj:socket对象发送和接收数据
 :return:
 """
 cmd_split = cmd.split(" ") # 将字符串命令分割成列表,用于验证命令是否存在
 if hasattr(self,cmd_split[0]):
 run_fun = getattr(self,cmd_split[0])
 run_fun(cmd_split,socket_obj)
 else:
 print("无效的命令")
 ...

总结

以上所述是小编给大家介绍的python 实现简单的FTP程序,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
Python 代码性能优化技巧分享
Aug 07 Python
在Python中处理字符串之isdigit()方法的使用
May 18 Python
python实现搜索本地文件信息写入文件的方法
Feb 22 Python
详解python里使用正则表达式的分组命名方式
Oct 24 Python
Python cookbook(数据结构与算法)保存最后N个元素的方法
Feb 13 Python
Python使用win32 COM实现Excel的写入与保存功能示例
May 03 Python
python使用Matplotlib画条形图
Mar 25 Python
对python中assert、isinstance的用法详解
Nov 27 Python
Python终端输出彩色字符方法详解
Feb 11 Python
python实现图像外边界跟踪操作
Jul 13 Python
Python sqlalchemy时间戳及密码管理实现代码详解
Aug 01 Python
再谈python_tkinter弹出对话框创建
Mar 20 Python
浅谈对pytroch中torch.autograd.backward的思考
Dec 27 #Python
python实现异常信息堆栈输出到日志文件
Dec 26 #Python
Python的对象传递与Copy函数使用详解
Dec 26 #Python
Python pandas库中的isnull()详解
Dec 26 #Python
python dataframe NaN处理方式
Dec 26 #Python
python实现大战外星人小游戏实例代码
Dec 26 #Python
Python数据存储之 h5py详解
Dec 26 #Python
You might like
PHP 数据结构 算法描述 冒泡排序 bubble sort
2011/07/10 PHP
php采集内容中带有图片地址的远程图片并保存的方法
2015/01/03 PHP
Laravel 5.1 on SAE环境开发教程【附项目demo源码】
2016/10/09 PHP
Juqery Html(),append()等方法的Bug解决方法
2010/12/13 Javascript
根据一段代码浅谈Javascript闭包
2010/12/14 Javascript
判断用户的在线状态 onbeforeunload事件
2011/03/05 Javascript
SeaJS入门教程系列之完整示例(三)
2014/03/03 Javascript
js取模(求余数)隔行变色
2014/05/15 Javascript
JavaScript中提前声明变量或函数例子
2014/11/12 Javascript
理解javascript中的严格模式
2016/02/01 Javascript
深入浅析JavaScript中的作用域和上下文
2016/03/26 Javascript
jQuery实现左右滑动的toggle方法
2018/03/03 jQuery
layui表单提交到后台自动封装到实体类的方法
2019/09/12 Javascript
基于Vue el-autocomplete 实现类似百度搜索框功能
2019/10/25 Javascript
[33:19]完美世界DOTA2联赛PWL S2 PXG vs InkIce 第一场 11.26
2020/11/30 DOTA
Python中的anydbm模版和shelve模版使用指南
2015/07/09 Python
用十张图详解TensorFlow数据读取机制(附代码)
2018/02/06 Python
python爬虫获取百度首页内容教学
2018/12/23 Python
如何使用Python进行OCR识别图片中的文字
2019/04/01 Python
Python2和Python3的共存和切换使用
2019/04/12 Python
python+openCV利用摄像头实现人员活动检测
2019/06/22 Python
Python如何爬取微信公众号文章和评论(基于 Fiddler 抓包分析)
2019/06/28 Python
Pytorch基本变量类型FloatTensor与Variable用法
2020/01/08 Python
Python 中的pygame安装与配置教程详解
2020/02/10 Python
jupyter notebook 实现matplotlib图动态刷新
2020/04/22 Python
python filecmp.dircmp实现递归比对两个目录的方法
2020/05/22 Python
浅谈keras中的目标函数和优化函数MSE用法
2020/06/10 Python
英国森林假期:Forest Holidays
2021/01/01 全球购物
Java里面Pass by value和Pass by Reference是什么意思
2016/05/02 面试题
《猫》教学反思
2014/02/26 职场文书
创建精神文明单位实施方案
2014/03/08 职场文书
民主生活会对照检查材料(统计局)
2014/09/21 职场文书
上课玩手机的检讨书
2014/10/01 职场文书
学校元旦晚会开场白
2015/05/29 职场文书
欠条样本
2015/07/03 职场文书
七年级作文之游记
2019/12/11 职场文书