基于socketsever下实现的FTP

  1 # ### 客户端client
  2 import socket
  3 import json
  4 import struct
  5 import os
  6 
  7 sk = socket.socket()
  8 sk.connect( ("127.0.0.1" , 9000) )
  9 
 10 
 11 def myrecv(info_len = 1024,sign=False):
 12     # 要考虑黏包
 13     if sign:
 14         # (1) 先接受发送数据的实际长度
 15         info_len = sk.recv(4) # 1
 16         # unpack 返回的是元组(1999,)
 17         info_len = struct.unpack("i",info_len)[0] 
 18     
 19     # (2) 接受服务端返回的数据
 20     file_info = sk.recv(info_len).decode() # 2
 21     file_dic = json.loads(file_info)
 22     return file_dic
 23 
 24 
 25 
 26 # 处理收发数据的逻辑
 27 def auth(opt):
 28     """
 29     opt: login 登录 or register 注册
 30     """
 31     usr = input("username: ").strip()
 32     pwd = input("password: ").strip()
 33     dic = {"user":usr,"pwd":pwd,"operate":opt}
 34     str_dic = json.dumps(dic)
 35     # 把数据发送给服务端
 36     sk.send(str_dic.encode())
 37     return myrecv()
 38 
 39     
 40 # 1.注册
 41 # res = auth("register")
 42 # print(res)
 43 
 44 def register():
 45     res = auth("register")
 46     return res
 47     
 48 # 2.登录
 49 # res = auth("login")
 50 # print(res)
 51 def login():
 52     res = auth("login")
 53     return res
 54 
 55 # 3.退出
 56 def myexit():
 57     opt_dic = {"operate":"myexit"}
 58     sk.send(json.dumps(opt_dic).encode())
 59     exit("欢迎下次再来 ... ")
 60 # myexit()
 61 
 62 # 4.下载
 63 def download():
 64     operate_dict = {
 65     "operate":"download",
 66     "filename":"ceshivideo.mp4"
 67     }
 68     
 69     opt_str = json.dumps(operate_dict)
 70     sk.send(opt_str.encode("utf-8"))
 71     
 72     # (1) 为了避免黏包,接受两次数据
 73     res = myrecv(sign=True)
 74     print(res) # {'result': True, 'info': '改文件存在'}
 75     if res["result"]:
 76         # 创建文件夹
 77         try:
 78             os.mkdir("mydownload")
 79         except:
 80             pass
 81             
 82         # (2) 接受文件名字 和 文件大小
 83         dic = myrecv(sign=True)
 84         print(dic,"<2>") # {'filename': 'ceshivideo.mp4', 'filesize': 54822153} <2>
 85     
 86         # (3) 接受文件数据
 87         with open("./mydownload/" + dic["filename"],mode="wb") as fp:
 88             while dic["filesize"]:
 89                 content = sk.recv(1024)
 90                 fp.write(content)
 91                 dic["filesize"] -= len(content)
 92         print("客户端下载完毕")
 93     else:
 94         print("文件不存在")
 95     
 96     
 97     
 98 
 99 # 第一套界面
100 operate_lst1 = [ ("登录",login) , ("注册",register) ,("退出",myexit)]
101 # 第二套界面
102 operate_lst2 = [("下载",download),("退出",myexit)]
103 """
104 ( 1, ('登录', <function login at 0x00000234890402F0>)   )
105 (2, ('注册', <function register at 0x0000023489040268>))
106 (3, ('退出', <function myexit at 0x0000023489040378>))
107 """
108 def main(operate_lst):
109     for i,tup in enumerate(operate_lst,start=1):
110         print(i,tup[0])
111         
112     num = int(input("请选择要执行的号码:>>>").strip()) 
113     # 调用函数
114     res = operate_lst[num-1][1]() # login()
115     # 返回结果
116     return res
117 
118 
119 
120 # 循环调用1号操作界面
121 while True:
122     # 开启第一套操作界面
123     res = main(operate_lst1)
124     print(res)
125     # 开启第二套操作界面
126     if res["result"]:
127         while True:
128             res = main(operate_lst2)
129             print(res)
130 
131 sk.close()

 

  1 # ### 服务端   server
  2 import socketserver
  3 import json
  4 import os
  5 import hashlib
  6 import struct
  7 
  8 # print(os.getcwd())
  9 print(__file__)
 10 print(os.path.dirname(__file__))
 11 base_path = os.path.dirname(__file__)
 12 
 13 # 获取当前账户文件所在的完整绝对路径
 14 userinfo = os.path.join(base_path,"db","userinfo.txt")
 15 print(userinfo)
 16 """"""
 17 class Auth():
 18     
 19     @staticmethod
 20     def md5(usr,pwd):
 21         md5_obj = hashlib.md5(usr.encode())
 22         md5_obj.update(pwd.encode())
 23         return md5_obj.hexdigest()
 24 
 25     # 注册
 26     @classmethod
 27     def register(cls,opt_dic) :
 28         # opt_dic = {'user': 'lisi', 'pwd': '222', 'operate': 'register'}
 29         # 1.监测数据库当中是否存在该用户
 30         with open(userinfo,mode="r",encoding="utf-8") as fp:            
 31             for line in fp:
 32                 username = line.split(":")[0] 
 33                 if username == opt_dic["user"]:
 34                     return {"result":False,"info":"用户名已经存在了"}
 35                     
 36         # 2.当前这个用户允许注册
 37         with open(userinfo,mode="a+",encoding="utf-8") as fp:
 38             # 用户名:密码 换行
 39             fp.write("%s:%s\n" % (opt_dic["user"] , cls.md5(opt_dic["user"],opt_dic["pwd"])))
 40         
 41         """
 42             当用户上传文件的时候,动态给用户创建文件夹
 43         """
 44         
 45         # 3.直接返回对应的状态
 46         return {"result":True,"info":"注册成功"}
 47         
 48     # 登录
 49     @classmethod
 50     def login(cls,opt_dic):
 51         # opt_dic = {'user': 'lisi', 'pwd': '222', 'operate': 'register'}
 52         with open(userinfo,mode="r",encoding="utf-8") as fp:
 53             for line in fp:
 54                 username,password = line.strip().split(":")
 55                 if username == opt_dic["user"] and password == cls.md5(opt_dic["user"],opt_dic["pwd"]):
 56                     return {"result":True,"info":"登陆成功"}
 57             return {"result":False,"info":"登录失败"}
 58             
 59     # 退出
 60     @classmethod
 61     def myexit(cls,opt_dic):
 62         return {"result":"myexit"}
 63 
 64 class FTPServer(socketserver.BaseRequestHandler):
 65     def handle(self):
 66         while True:
 67             # 接受服务端发送过来的数据
 68             opt_dic = self.myrecv()
 69             print(opt_dic)
 70             
 71             # 判断类当中是否含有register这个方法,如果有就反射
 72             if hasattr(Auth,opt_dic["operate"]):
 73                 # getattr 反射出对应的register函数
 74                 res = getattr(Auth,opt_dic["operate"])(opt_dic)
 75                 
 76                 # 判断是myexit,代表退出
 77                 if res["result"] == "myexit":
 78                     return
 79                 
 80                 # 把返回值发送给客户端
 81                 self.mysend(res)
 82                 
 83                 # 等待处理用户第二套操作界面的逻辑
 84                 if res["result"]:
 85                     while True:
 86                         opt_dic = self.myrecv()
 87                         print(opt_dic)
 88                         
 89                         # 如果operate接受的是myexit 直接终止handle函数;
 90                         if opt_dic["operate"] == "myexit":
 91                             return                 
 92                         
 93                         # 可以反射 类 或者 对象 或者 模块响应的属性和方法
 94                         if hasattr(self,opt_dic["operate"]):
 95                             getattr(self,opt_dic["operate"])(opt_dic)
 96                 
 97                 
 98             # 如果Auth当中没有该操作的应急处理
 99             else:
100                 dic = {"result":False,"info":"没有该操作"}
101                 self.mysend(dic)
102 
103                 
104             
105         
106     # 接受数据的方法
107     def myrecv(self):
108         info = self.request.recv(1024)
109         opt_str = info.decode()
110         opt_dic = json.loads(opt_str)
111         return opt_dic
112         
113     # 发送数据的方法
114     def mysend(self,send_info,sign=False):
115         # 先把字典变成字符串,然后变成字节流发送出去
116         send_info = json.dumps(send_info).encode()
117         
118         # 增加代码
119         if sign:
120             # 通过pack转化成居右固定4个字节长度的字节流
121             res = struct.pack("i",len(send_info))
122             # 把接下来要发送的数据长度发送给客户端
123             self.request.send(res) # 1
124         
125         # 发送出去
126         self.request.send(send_info) # 2
127         
128     # 下载方法
129     def download(self,opt_dic):
130         # {'operate': 'download', 'filename': 'ceshivideo.mp4'}
131         # print(opt_dic)
132         # 先判断是否存在该文件
133         filename = opt_dic["filename"]
134         file_abs = os.path.join(base_path,"myvideo",filename)
135         # print(filename) # ceshivideo.mp4
136         # print(file_abs) # D:/gongxiang8/day31/ftp\myvideo\ceshivideo.mp4
137         
138         if os.path.exists(file_abs):
139             # 1.先告诉用户,可以操作,存在该文件
140             dic = {"result":True,"info":"改文件存在"}
141             self.mysend(dic,sign=True)
142             
143             # 2.把对应的文件名,以及文件的大小发过去
144             filesize = os.path.getsize(file_abs)
145             dic = {"filename":filename,"filesize":filesize}
146             self.mysend(dic,sign=True)
147             
148             
149             # 3.真正开始发送文件内容
150             with open(file_abs,mode="rb") as fp:
151                 while filesize:
152                     # 一次性最多读取1024个字节
153                     content = fp.read(1024)
154                     self.request.send(content)
155                     filesize -= len(content)
156             print("服务器下载完毕")
157             
158         else:
159             dic = {"result":False,"info":"改文件不存在"}
160             self.mysend(dic,True)
161         
162         
163         
164         
165         
166         
167         
168 myserver = socketserver.ThreadingTCPServer( ("127.0.0.1" , 9000)  , FTPServer)
169 # 让一个端口绑定多个程序(测试时使用)
170 socketserver.TCPServer.allow_reuse_address = True
171 myserver.serve_forever()

 

 

 

 

 

 

 

 

 

 

posted @ 2019-11-26 20:27  zyling_me  阅读(207)  评论(0编辑  收藏  举报