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


    客户端(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 = '33[32;1m $' + login_uname + '>>:33[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:{},	 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.数据库截图:

      

      2.客户端截图:

      

      3.服务端截图:

      

  • 相关阅读:
    spring @Primary-在spring中的使用(十九)
    Java中lombok @Builder注解使用详解(十八)
    Spring Boot的MyBatis注解:@MapperScan和@Mapper(十七)
    js基础只是总结-语句
    js基础知识-数据类型
    启动redis服务报错Creating Server TCP listening socket *:6379: bind: Address already in use [duplicate]
    gitlab 配置SSH和ACCESS TOKEN
    https nginx配置
    Vue和React区别
    深入虚拟DOM和DOM-diff
  • 原文地址:https://www.cnblogs.com/g177w/p/8137660.html
Copyright © 2020-2023  润新知