模拟FTP服务器 socketserver
(1)登录 (2)注册 (3) 上传[待补充] (4) 下载
server.py
# 服务端
import socketserver
import json
import hashlib
import os
import struct
# 找当前数据库文件所在的绝对路径
base_path = os.path.dirname(__file__)
# /mnt/hgfs/python31_gx/day36/db/userinfo.txt
userinfo = os.path.join(base_path,"db","userinfo.txt")
class Auth():
@staticmethod
def md5(usr,pwd):
md5_obj = hashlib.md5(usr.encode())
md5_obj.update(pwd.encode())
return md5_obj.hexdigest()
@classmethod
def register(cls,opt_dic):
# 1.检测注册的用户是否存在
with open(userinfo,mode="r",encoding="utf-8") as fp:
for line in fp:
username = line.split(":")[0]
if username == opt_dic["user"]:
return {
"result":False,"info":"用户名存在了"}
# 2.当前用户可以注册
with open(userinfo,mode="a+",encoding="utf-8") as fp:
strvar = "%s:%s\n" % ( opt_dic["user"] , cls.md5( opt_dic["user"],opt_dic["passwd"] ) )
fp.write(strvar)
"""
当用户上传的时候,给他创建一个专属文件夹,存放数据
"""
# 3.返回状态
return {
"result":True,"info":"注册成功"}
@classmethod
def login(cls,opt_dic):
with open(userinfo , mode="r" , encoding="utf-8") as fp:
for line in fp:
username,password = line.strip().split(":")
if username == opt_dic["user"] and password == cls.md5( opt_dic["user"] , opt_dic["passwd"] ) :
return {
"result":True,"info":"登录成功"}
return {
"result":False,"info":"登录失败"}
@classmethod
def myexit(cls,opt_dic):
return {
"result":"myexit"}
class FTPServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
opt_dic = self.myrecv()
print(opt_dic) # {'user': 'wangwen', 'passwd': '111', 'operate': 'register'}
if hasattr(Auth,opt_dic["operate"]):
# print( getattr(Auth,"register") )
res = getattr(Auth,opt_dic["operate"])(opt_dic) # login(opt_dic)
# 如果接受的操作是myexit,代表退出
if res["result"] == "myexit":
return
# 把状态发送给客户端
self.mysend(res)
# 开启第二套操作界面的逻辑
if res["result"] :
while True:
# 接受用户发过来的第二套界面的数据
opt_dic = self.myrecv()
print(opt_dic) # {'operate': 'download', 'filename': 'ceshi123.mp4'}
if opt_dic["operate"] == "myexit":
return
# 通过FTPServer反射出下载的方法
""""""
if hasattr(self,opt_dic["operate"]):
getattr(self,opt_dic["operate"])(opt_dic)
else:
dic = {
"result":False,"info":"没有该操作"}
self.mysend(dic)
# 接收方法
def myrecv(self):
info = self.request.recv(1024)
opt_str = info.decode()
opt_dic = json.loads(opt_str)
return opt_dic
# 发送方法
def mysend(self,send_info,sign=False):
send_info = json.dumps(send_info).encode()
if sign:
res = struct.pack("i",len(send_info))
# 1.发送数据的长度
self.request.send(res)
# 2.发送真实的数据
self.request.send(send_info)
def download(self,opt_dic):
filename = opt_dic["filename"]
# (1) 判断文件是否存在
# 拼接绝对路径
file_abs = os.path.join(base_path,"video",filename)
if os.path.exists(file_abs):
# 1号.告诉用户,文件存在,可以下载
dic = {
"result":True,"info":"文件存在,可以下载"}
self.mysend(dic,sign=True)
# 2号.发送 文件名字 和 文件大小
filesize = os.path.getsize(file_abs)
dic = {
"filename":filename , "filesize":filesize}
self.mysend(dic,sign=True)
# 3号.真正开始发送数据
with open(file_abs,mode="rb") as fp:
while filesize:
content = fp.read(1024000)
self.request.send(content)
filesize -= len(content)
print("服务器下载完毕")
else:
dic = {
"result":False,"info":"文件不存在"}
self.mysend(dic,sign=True)
# 设置一个端口可以绑定多个程序
# socketserver.TCPServer.allow_reuse_address = True
myserver = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , FTPServer)
myserver.serve_forever()
client.py
# ### 客户端
import socket
import json
import struct
import os
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
def myrecv(info_len = 1024 , sign=False):
if sign:
# 1. 接受发送数据的实际长度
info_len = sk.recv(4)
info_len = struct.unpack("i",info_len)[0]
# 2.接受服务端响应的真实的数据
file_info = sk.recv(info_len).decode()
file_dic = json.loads(file_info)
return file_dic
# 处理收发数据的逻辑
def auth(opt):
usr = input("username: ").strip()
pwd = input("password: ").strip()
dic = {
"user":usr,"passwd":pwd,"operate":opt}
str_dic = json.dumps(dic)
# 发送数据
sk.send(str_dic.encode("utf-8"))
return myrecv()
# 注册
def register():
res = auth("register")
return res
# 登录
def login():
res = auth("login")
return res
# 退出
def myexit():
opt_dic = {
"operate":"myexit"}
sk.send(json.dumps(opt_dic).encode())
exit("欢迎下次再来")
def download():
operate_dict = {
"operate" : "download",
"filename":"ceshi123.mp4"
}
# 把要下载的数据给服务端
opt_str = json.dumps(operate_dict)
sk.send(opt_str.encode("utf-8"))
# 接受服务端发送过来的数据 (是否可以操作)[对应服务端的1号发送]
res = myrecv(sign=True)
# {'result': True, 'info': '文件存在,可以下载'}
print(res)
#
# (1) 如果文件存在,在本地创建一个文件夹
if res["result"]:
try:
os.mkdir("mydownload")
except:
pass
# (2) 接受 文件名字 和 文件大小 对应服务端的2号发送
dic = myrecv(sign=True)
print(dic)
# (3) 接受真实的文件 对应服务端的3号发送
with open("./mydownload/" + dic["filename"],mode="wb") as fp:
while dic["filesize"]:
content = sk.recv(1024000)
fp.write(content)
dic["filesize"] -= len(content)
print("客户端下载完毕")
else:
print("没有该文件")
# 第一套操作界面
# 0 1 2
operate_lst1 = [ ("登录",login) ,("注册",register) , ("退出",myexit) ]
# 第二套操作界面
operate_lst2 = [ ("下载",download) , ("退出",myexit)]
"""
1.登录
2.注册
3.退出
1 ('登录', <function login at 0x7ff7cf171a60>)
2 ('注册', <function register at 0x7ff7cf17e620>)
3 ('退出', <function myexit at 0x7ff7cf171ae8>)
"""
def main(operate_lst):
for i,tup in enumerate(operate_lst,start=1):
print(i , tup[0])
num = int(input("请选择执行的操作>>> ").strip()) # 1 2 3
# 调用函数
# print(operate_lst1[num-1]) ('退出', <function myexit at 0x7f801e34aa60>)
# print(operate_lst1[num-1][1]) <function myexit at 0x7f801e34aa60>
# operate_lst1[num-1][1]() myexit()
res = operate_lst[num-1][1]()
return res
while True:
# 开启第一套操作界面
res = main(operate_lst1)
""""""
if res["result"]:
# 开启第二套操作界面
while True:
res = main(operate_lst2)
sk.close()