socketserver+socket实现较为复杂的ftp,支持多用户在线

时间:2022-08-29 12:01:32

客户端(ftp_client.py)

  1 import socketserver,json,hashlib,os
  2 from pymongo import MongoClient
  3 
  4 '''
  5 *****要点*****
  6 1.面向对象编程
  7 2.反射的利用
  8 3.代码的高度解耦
  9 4.md5加密传输认证
 10 5.数据库查询验证
 11 **************
 12 '''
 13 db = MongoClient('localhost', 27017)
 14 curent_document = ''
 15 #此类用于处理一系列客户端请求
 16 class deal():
 17     def __init__(self,name):
 18         self.name = name
 19 
 20     #用户登录认证
 21     def deal_login(self,data,client):
 22         global logined_uname,db
 23         BASE_DIR = ''#基目录
 24         #选择数据库
 25         d = db['DB_FOR_PYTHON']
 26         #选择集合
 27         users = d['FTP_USERS']
 28         name = data['name']
 29         pwd = data['pwd']
 30         u = users.find_one({'name':name ,'pwd':pwd})
 31         #用户不存在,返回0
 32         if  u == None or u == '':
 33             #使用find()和find_one返回的结果分别是cursor和一条记录
 34             u1 = users.find_one({'name':name})
 35             if u1 == None or u1 == '':
 36                 #无此用户
 37                 client.send(b'0')
 38                 return '0'.encode()
 39             else:
 40                 #密码有误
 41                 client.send(b'2')
 42                 return '2'.encode()
 43         else:
 44             #登录成功
 45             client.send(b'1')
 46             logined_uname = name
 47             return  '1'.encode()
 48 
 49     #上传处理
 50     def deal_upload(self,data,request):
 51         #查出用户的磁盘配额大小,并检查用户上传文件大小,和要上传的文件加以对比,看是否可以继续上传
 52         user1 = db.DB_FOR_PYTHON.FTP_USERS.find_one({'name':logined_uname})
 53         #用户的磁盘配额大小
 54         space_limite = user1['space']
 55         print('当前用户磁盘总空间:{}'.format(str(space_limite)))
 56         name = data['name']
 57         #上传文件大小
 58         totalsize = data['size']*1024
 59         m = hashlib.md5()
 60         request.send('ok'.encode())
 61         cur_size = 0
 62         cur_content =b''
 63         size = 0
 64         global BASE_DIR
 65         BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 66         file_name = ''
 67         #剩余磁盘大小
 68         space = 0
 69         u_dir = BASE_DIR+os.sep+'USERS_HOME'+os.sep+logined_uname.upper()+"_HOME"
 70         if os.path.exists(u_dir):
 71             # 用户总目录文件夹大小(bit计算)
 72             real_space = (os.stat(u_dir).st_size)*1024
 73             space = space_limite - real_space
 74             file_name = u_dir+os.sep+name
 75         else:
 76             os.mkdir(u_dir)
 77             space = space_limite
 78             file_name = u_dir + os.sep + name
 79         #如果上传资源的大小小于等于磁盘剩余空间,则继续
 80         if space >= totalsize:
 81             print('可以发送')
 82             f = open(file_name,'wb')
 83             while cur_size < totalsize:
 84                 if totalsize - cur_size >1024*1024:
 85                     size = 1024*1024
 86                 else:
 87                     size = totalsize - cur_size
 88                 print(size)
 89                 data = request.recv(size)
 90                 m.update(data)
 91                 cur_content += data
 92                 f.write(data)
 93                 cur_size += len(data)*1024
 94             else:
 95                 print('haha')
 96                 f.close()
 97                 request.send(json.dumps({'state':'upload success!','md5':m.hexdigest()}).encode())
 98                 return json.dumps({'state':'upload success!','md5':m.hexdigest()}).encode()
 99         else:
100             request.send(json.dumps({'state':'upload fail,your filesize exceeding the rest of your own space!','rest':space,'filesize':totalsize,'md5':None}).encode())
101             return  json.dumps({'state':'upload fail,your filesize exceeding the rest of your own space!','rest':space,'filesize':totalsize,'md5':None}).encode()
102     #下载实现
103     def deal_download(self,data):
104         self.data = data['name']
105         return self.data
106     #用户切换目录
107     def deal_cd(self,data):
108         global BASE_DIR,curent_document
109         #当前用户基目录
110         ROOT_DOC = BASE_DIR+os.sep+'USERS_HOME'+os.sep+logined_uname.upper()+"_HOME"
111         current_document = ROOT_DOC
112         #用户输入的目录
113         manual_enter = data['cd_document']
114         current_document = os.chdir(manual_enter) if os.path.exists(manual_enter) else curent_document
115         print("用户要跳转的目录:",current_document)
116         pass
117     #查看文件
118     def deal_dir(self):
119         pass
120     #断点续传功能
121     def deal_cont(self):
122         pass
123 #面向对象的方式实现
124 class ftp_server(socketserver.BaseRequestHandler):
125     def handle(self):
126         logined_uname = ''
127         while True:
128             try:
129                 #接收来自客户端的信息
130                 data = self.request.recv(1024).decode()
131                 data = json.loads(data)
132                 print(data)
133                 cmd = data['req']
134                 #创建服务端对象
135                 d = deal('ser')
136                 if hasattr(d,cmd):
137                     c = getattr(d,cmd)
138                     res = c(data,self.request).decode()
139                     print(res)
140             except ConnectionResetError as e:
141                 print('error:',e)
142                 break
143 
144  #主方法入口
145 if __name__ ==  '__main__':
146     HOST,PORT = 'localhost',9999
147     server = socketserver.ThreadingTCPServer((HOST,PORT),ftp_server)
148     server.serve_forever()

 

服务端(ftp_server.py

 1 # coding  = utf-8
 2 
 3 import socket,json,re,os,hashlib
 4 login_uname = ''
 5 class client_do(object):
 6     def __init__(self,name):
 7         self.name = name
 8     #登录
 9     def deal_login(self,data,client):
10         try:
11             global login_uname
12             global state
13             name = data.split()[1].split('\\')[0]
14             pwd = data.split()[1].split('\\')[1]
15             req = data.split()[0]
16             data = {
17                 'req':req,
18                 'name':name,
19                 'pwd':pwd
20             }
21             client.send(json.dumps(data).encode())
22             response = client.recv(1024).decode()
23             if response == '1':
24                 login_uname = name
25                 global lg_s
26                 lg_s = '\033[32;1m $' + login_uname + '>>:\033[0m'
27             state = response
28             #返回结果可能为0(无此用户),1(验证成功),2(密码有误)
29             return response
30         except Exception as e:
31             print("登录出错:",e)
32     #上传
33     def deal_upload(self,mes,client):
34          try:
35              if state == '1':
36                  filename = mes.split()[1]
37                  if os.path.isfile(filename):
38                      f = open(filename, 'rb')
39                      filesize = os.stat(filename).st_size
40                      print('filesize:', filesize)
41                      data = {
42                          'req': mes.split()[0],
43                          'name': filename,
44                          'size': filesize,
45                          'overwrite': True
46                      }
47                      client.send(json.dumps(data).encode())
48                      # 接受服务器返回的信息,防止粘包,同时得到磁盘是否够传信息
49                      res = client.recv(1024)
50                      print('first response:', res.decode())
51                      # 准备发送信息
52                      m = hashlib.md5()
53                      for line in f:
54                          client.send(line)
55                          m.update(line)
56                      md5_client = m.hexdigest()
57                      f.close()
58                      # 接受服务器请求处理结果
59                      response_info = client.recv(1024).decode()
60                      response_info = json.loads(response_info)
61                      md5_server = response_info['md5']
62                      print('md5_client:{},\t md5_server:{}'.format(md5_client, md5_server))
63                      print("状态:{}".format(response_info['state']))
64                      #上传ok
65                      return '1'
66                  else:
67                      #上传出错
68                      return '0'
69              else:
70                  print('请先完成登录')
71          except Exception as e:
72              print('上传出错:',e)
73     #用户切换目录
74     def deal_cd(self):
75         pass
76     #查看文件
77     def deal_dir(self):
78         pass
79     #断点续传功能
80     def deal_cont(self):
81         pass
82 client = socket.socket()
83 client.connect(('localhost',9999))
84 state = 0
85 lg_s =">>:"
86 while True:
87     mes = input(lg_s)
88     if mes == '' or mes ==None:continue
89     cl_req = mes.split()[0]
90     c = client_do('client')
91     if hasattr(c,cl_req):
92         s = getattr(c,cl_req)
93         res = s(mes,client)
94     else:
95         print('无效的命令')

说明:

代码实现的功能有用户登录认证,上传的前提是要登录,文件上传,用户磁盘配额,断点续传和目录跳转功能暂未实现。后续实现补上

运行截图:

  1.数据库截图:

  socketserver+socket实现较为复杂的ftp,支持多用户在线

  2.客户端截图:

  socketserver+socket实现较为复杂的ftp,支持多用户在线

  3.服务端截图:

  socketserver+socket实现较为复杂的ftp,支持多用户在线