python 实现简单的FTP程序
FTP即文件传输协议;它基于客户机-服务器模型体系结构,应用广泛。它有两个通道:一个命令通道和一个数据通道。命令通道用于控制通信,数据通道用于文件的实际传输。使用FTP可以做很多事情,比如移动、下载、复制文件等。
一、开发环境
server端:centos7 python-3.6.2
客户端:Windows7python-3.6.2pycharm-2018
程序目的:1、学习使用socketserver实现并发处理多个客户端。
2、了解使用struct解决TCP粘包。
二、程序设计
(本人菜鸟一枚,对于开发规范,接口设计完全不懂,完全是随心所欲,自娱自乐。写博客主要是记录自己学习的点点滴滴,如有不足之处还请见谅。)
1、server端
1.1目录结构如下:
1.2目录简介:
FTP_SERVER:程序主目录
app:程序主逻辑目录,目录下有四个模块:
FTPserver.py:FTP Server端启动入口。
login.py:认证注册模块,用于处理用户注册,登录认证。
dataAnalysis.py:命令解析模块,负责解析,执行客户端命令。
FileOpertion.py:负责文件读,写。数据发送,数据接收。
db:存放user_pwd.db文件,用于存放用户信息(用户名,密码,FTP目录总空间,已使用空间等)
lib:存放公共数据。
1.3模块中类的继承关系
1.4执行流程
1.4.1程序启动文件FTPserver.py,程序启动后进入监听状态。核心代码如下:
classMyFtpServer(socketserver.BaseRequestHandler):
defhandle(self):#重写handle方法,处理socket请求
print(f"连接来自{self.client_address}的客户端")
commom_obj=Commom()
data_analy=DataAnalysis()
login_obj=Login()
while1:
#执行用户选项:1、登陆系统2、注册账号。并返回一个结果
status_id=login_obj.run_client_choice(self.request,commom_obj)
ifstatus_id=="01":#登陆成功
ifnotself.run_ftp_server(data_analy,commom_obj):#执行ftpserver主功能
break
elifint(status_id)==-1:#client断开连接了
break
print(f"客户端{self.client_address}断开了连接")
defrun_ftp_server(self,data_analy,commom_obj):
""""
登陆成功后,接收客户端发来的命令,并进行处理
:paramdata_analy:负责解析,执行客户端命令的对象
:paramcommom_obj:程序执行时所需的数据对象
:return返回false代表客户端断开连接了
"""
whileTrue:
try:
cmd_len_pack=self.request.recv(4)
cmd_len=struct.unpack('i',cmd_len_pack)[0]#获取命令长度,防止粘包
exceptException:
break
recv_data=self.request.recv(cmd_len).decode('utf-8')#接收客户端数据
ifrecv_data.upper()=="Q":#客户端提出断开连接了
break
#解析,处理客户端的命令
data_analy.syntax_analysis(recv_data,self.request,commom_obj)
returnFalse
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核心代码如下:
classLogin(FileOperation):
"""
登陆注册类。主要负责用户的登陆认证,和用户注册。
"""
defrun_client_choice(self,socket_obj,commom):
"""
获取客户端的请求,1是登陆,2是注册用户
:paramsocket_obj:socket对象
:paramcommom:ftpserver运行时所需要的数据对象
:return:
"""
recv_choice=socket_obj.recv(1).decode("utf-8")#获取用户选项:1是登陆,2是注册用户
ifrecv_choice=="1":#client请求登陆
returnself.login_authen(socket_obj,commom)
elifrecv_choice=="2":#client请求注册账号
returnself.register_user(socket_obj,commom)
else:
return-1#client断开连接了
#用户登陆认证
deflogin_authen(self,socket_obj,commom):
"""
客户端登陆认证
:paramsocket_obj:socket对象
:paramcommom: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)
ifcheck_ret:#用户名密码正确
self.check_user_home_dir(commom,recv_userPwd[0])#检测用户家目录
returncommom.status_info["login_success"]
else:
returncommom.status_info["login_fail"]
...
#注册用户
defregister_user(self,socket_obj,commom):
"""
:paramsocket_obj:
:paramcommom:
:return:返回是否允许注册的结果,1允许客户端注册,2拒绝客户端注册
"""
whileTrue:
choice_id=input("请输入回应码:1是允许注册,2是不允许注册:")
ifchoice_id.isdigit()and3>int(choice_id)>0:
socket_obj.send(choice_id.encode("utf-8"))#发通知告知客户端,处理结果
ifchoice_id=="1":#注册用户
returnself.client_register(socket_obj,commom)
returnchoice_id
else:
print("您输入的信息有误,请重新输入。")
...
1.4.3客户端登录成功后,服务端会等待接收客户端发来的命令,命令的解析,执行由dataAnalysis.py模块执行,核心代码如下:
classDataAnalysis(FileOperation):
"""
数据分析处理类,主要负责解析client发送过来的指令。
"""
defsyntax_analysis(self,recv_data,socket_obj,commom):
"""
负责解析客户端传来的数据。
:paramrecv_data:接收到的客户端用户数据
:paramsocket_obj:socket对象
:paramcommom:数据对象
:return:
"""
clientData=recv_data.split("")
ifhasattr(self,clientData[0]):#判断对象方法是否存在
get_fun=getattr(self,clientData[0])#获取对象方法
get_fun(clientData,socket_obj,commom)#运行对象方法
else:
pass
...
执行客户端命令后,继续等待接收客户端发来的命令,如此循环...。
2、客户端
2.1目录结构如下:
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模块中类的继承关系
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.2FTPclient.py模块下的run_ftp_client方法会打印菜单,并等待用户输入选项,执行相应功能,核心代码如下:
classClient(Login,DataAnalysis):
defrun_ftp_client(self,socket_obj):
"""
运行用户输入的选项:1、是登陆2、是注册账号
:return:
"""
whileTrue:
self.login_menu()#打印系统菜单
choice_id=self.get_user_choice()#获取用户输入的选项
ifchoice_id:
ifself.run_user_choice(choice_id,socket_obj):
break
else:
print("您输入的有误")
defget_user_choice(self):
"""
获取用户输入的选项
:return:
"""
choice_id=input("请输入选项:")
ifchoice_id.isdigit()and4>int(choice_id)>0orchoice_id.upper()=="Q":
returnchoice_id
returnFalse
defrun_user_choice(self,choice_id,socket_obj):
ifchoice_id=="1":#登陆系统
socket_obj.send(choice_id.encode("utf-8"))#发通知告知服务器准备登陆
ifself.run_login(socket_obj)==True:#执行登陆
returnTrue
elifchoice_id=="2":#注册用户
socket_obj.send(choice_id.encode("utf-8"))#请求服务器,注册用户
self.register_user(socket_obj)#执行注册
elifchoice_id.upper()=="Q":#退出程序
socket_obj.send(choice_id.encode("utf-8"))#通知服务器,准备退出程序
socket_obj.close()
print("程序正常退出")
returnTrue
defrun_login(self,socket_obj,):
"""
运行登陆认证模块,如果登陆成功执行程序主逻辑,否则重新登陆。
:paramsocket_obj:
:return:
"""
ifself.login_authention(socket_obj):
whileTrue:
send_data=input(">>>").strip("")#获取发送数据(用户执行的命令)
ifsend_data.upper()=="Q":#正常退出程序
socket_obj.send(send_data.encode("utf-8"))#通知服务区断开连接
socket_obj.close()
print("程序正常退出")
returnTrue
ifself.syntax_analysis(send_data,socket_obj):#解析用户数据并处理数据
print("异常退出")
returnTrue
returnFalse
deflogin_menu(self):
print("-"*41)
print("欢迎登陆迷你FTPv1.0")
print("-"*41)
print("1、登陆系统")
print("2、用户注册")
print("Q、退出程序")
2.4.3login.py模块主要用于处理注册和登录的功能,核心代码如下:
classLogin(Commom):
deflogin_authention(self,socket_obj):
"""
登陆认证
:paramsocket_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])#打印状态码对应的结果
ifself.status_info[recv_status]=='登录成功':
returnTrue
returnFalse
...
defregister_user(self,socket_obj):
"""
等待服务端反馈是否允许注册用户。
:paramsocket_obj:
:return:
"""
print("请等待服务端回应.....")
recv_status=socket_obj.recv(1).decode("utf-8")
ifrecv_status=="1":#服务端同意申请账号
user_pwd=self.get_regist_user_pwd()#获取注册用户名和密码
ifuser_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("服务端拒绝了您申请账号的请求,请与管理员取得联系。")
returnFalse
...
2.4.4用户登录成功后,会等待接收用户输入命令,由dataAnalysis.py模块负责解析用户输入的命令,并将命令发给FTP服务器,然后接收服务器的反馈。核心代码如下:
classDataAnalysis(FileOperation):
defsyntax_analysis(self,cmd,socket_obj):
"""
解析用户输入的命令。
:paramcmd:用户执行的命令,如:put上传的文件
:paramsocket_obj:socket对象发送和接收数据
:return:
"""
cmd_split=cmd.split("")#将字符串命令分割成列表,用于验证命令是否存在
ifhasattr(self,cmd_split[0]):
run_fun=getattr(self,cmd_split[0])
run_fun(cmd_split,socket_obj)
else:
print("无效的命令")
...
总结
以上所述是小编给大家介绍的python实现简单的FTP程序,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。