zoukankan      html  css  js  c++  java
  • python第四十八天--高级FTP

    高级FTP服务器
    1. 用户加密认证
    2. 多用户同时登陆
    3. 每个用户有自己的家目录且只能访问自己的家目录
    4. 对用户进行磁盘配额、不同用户配额可不同
    5. 用户可以登陆server后,可切换目录
    6. 查看当前目录下文件
    7. 上传下载文件,保证文件一致性
    8. 传输过程中现实进度条
    9.支持断点续传
    10.用户操作日志

    服务端 启动参数 start
    客户端 启动参数 -s localhost -P 9500

    程序结构:
    seniorFTP/#综合目录
    |- - -ftp_client/#客户端程序目录
    | |- - -__init__.py
    | |- - -bin/#启动目录
    | | |- - -__init__.py
    | | |- - -client_ftp.py#客户端视图启动
    | |
    | |- - -cfg/#配置目录
    | | |- - -__init__.py
    | | |- - -config.py#配置文件
    | |
    | |- - -down/#下载文件目录
    | |
    | |- - -putfile/#上传文件目录
    | |
    | |
    | |- - -REDMAE
    |- - -ftp_server/#服务端程序目录
    | |- - -__init__.py
    | |- - -bin/#启动目录
    | | |- - -__init__.py
    | | |- - -start.py#服务端视图启动
    | | |- - -user_reg.py#用户注册启动
    | |
    | |- - -cfg/#配置目录
    | | |- - -__init__.py
    | | |- - -config.py#配置文件
    | | |- - -userpwd.cfg#用户信息文件
    | |
    | |- - -core/#文件目录
    | | |- - -__init__.py
    | | |- - -ftp_server.py#服务端主要逻辑 类
    |           |      |- - -logs.py#日志主要逻辑 类
    |           |      |- - -main.py#服务端启动主程序
    | |
    | |- - -home/#用户文件目录
    | | |- - -用户/#个人目录
    | |
    | |- - -log/#日志文件目录
    | |
    | |- - -REDMAE
    | |
    |
    |- - -REDMAE

    先上流程图:

    详细代码如下:

    |- - -ftp_client/#客户端程序目录
    | |- - -__init__.py
    | |- - -bin/#启动目录
    | | |- - -__init__.py
    | | |- - -client_ftp.py#客户端视图启动
    
    
      1 #!usr/bin/env python
      2 #-*-coding:utf-8-*-
      3 # Author calmyan
      4 import socket,os,json,getpass,hashlib
      5 import os ,sys,optparse
      6 
      7 STATUS_CODE={
      8     240:'格式出错,格式:{"action":"get","filename":"filename","size":100}',
      9     241:'指令错误',
     10     242:'用户密码出错',
     11     243:'用户或密码出错',
     12     244:'用户密码通过校验',
     13 }
     14 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     15 sys.path.append(BASE_DIR)#增加环境变量
     16 from cfg import config
     17 class FTPClient(object):
     18     def __init__(self):
     19         paresr=optparse.OptionParser()
     20         paresr.add_option('-s','--server',dest='server',help='服务器地址')
     21         paresr.add_option('-P','--port',type="int",dest='port',help='服务器端口')
     22         paresr.add_option('-u','--username',dest='username',help='用户名')
     23         paresr.add_option('-p','--password',dest='password',help='密码')
     24         (self.options,self.args)=paresr.parse_args()#返回一个字典与列表的元组
     25         self.verify_args(self.options,self.args)#判断参数
     26         self.ser_connect()#连接服务端
     27         self.cmd_list=config.CMD_LIST
     28         self.rat=0#文件断点
     29 
     30     #实例化一个连接端
     31     def ser_connect(self):
     32         self.c=socket.socket()#实例化一个连接端
     33         self.c.connect((self.options.server,self.options.port))#进行连接
     34 
     35     #判断用户与密码是否成对出现
     36     def verify_args(self,options,args):
     37         if (options.username is None and options.password is None) or (options.username is not None and options.password is not None):#判断用户与密码是否成对出现
     38             pass##判断用户与密码单个出现
     39         else:
     40             exit('出错:请输入用户与密码!')#退出
     41         if options.server and options.port:#端口判断
     42             if options.port>0 and options.port<65535:
     43                 return True
     44             else:
     45                 print('端口号:[%s]错误,端口范围:0-65535'%options.port)
     46 
     47     #登陆方法
     48     def landing(self):#登陆方法
     49         '''用户验证'''
     50         if self.options.username is not None:#判断用户名已经输入
     51             #print(self.options.username,self.options.password)
     52             return self.get_user_pwd(self.options.username,self.options.password)#返回结果
     53         else:
     54             print('用户登陆'.center(60,'='))
     55             ret_count=0#验证次数
     56             while ret_count<5:
     57                 username=input('用户名:').strip()
     58                 password=getpass.getpass('密码:').strip()
     59                 if self.get_user_pwd(username,password):
     60                     return self.get_user_pwd(username,password)#调用远程验证用户 返回结果
     61                 else:
     62                     ret_count+=1#次数加一
     63                     print('认证出错次数[%s]'%ret_count)
     64             else:
     65                 print('密码出错次数过多!')
     66                 exit()
     67 
     68     #'''用户名与密码检验'''
     69     def get_user_pwd(self,username,password):
     70         '''用户名与密码检验'''
     71         #发送 头文件
     72         data={
     73             'action':'auth',
     74             'username':username,
     75             'password':password
     76         }
     77         self.c.send(json.dumps(data).encode())#发送到服务器
     78         response = self.get_response()#得到服务端的回复
     79         if response.get('status_code') == 244:
     80             print(STATUS_CODE[244])
     81             self.user = username#存下用户名
     82             self.user_dir=response.get('dir')#目录
     83             return True
     84         else:
     85             print(response.get("status_msg") )
     86 
     87     #服务器回复
     88     def get_response(self):#服务器回复
     89         '''服务器回复信息'''
     90         data=self.c.recv(1024)#接收回复
     91         data = json.loads(data.decode())
     92         return data
     93 
     94     #指令帮助
     95     def help(self):#指令帮助
     96         attr='''
     97         help            指令帮助
     98         ----------------------------------
     99         info            个人信息
    100         ----------------------------------
    101         ls              查看当前目录(linux/windows)
    102         ----------------------------------
    103         pwd             查看当前路径(linux/windows)
    104         ----------------------------------
    105         cd 目录         切换目录(linux/windows)
    106         ----------------------------------
    107         get filename    下载文件
    108         ----------------------------------
    109         put filename    上传文件
    110         ----------------------------------
    111         --md5           使用md5  在get/put 后
    112         ----------------------------------
    113         mkdir name      创建目录(linux/windows)
    114         ----------------------------------
    115         rmdir name      删除目录(linux/windows)
    116         ----------------------------------
    117         rm filename     删除文件 (linux/windows)
    118         ----------------------------------
    119         exit            退出
    120         ----------------------------------
    121         '''.format()
    122         print(attr)
    123 
    124     ##交互
    125     def inter(self):#交互
    126         if  self.landing():#通过用户密码认证
    127             print('指令界面'.center(60,'='))
    128             self.help()
    129             while True:
    130                 cmd = input('[%s]-->指令>>>:'%self.user_dir).strip()
    131                 if len(cmd)==0:continue#输入空跳过
    132                 if cmd=='exit':exit()#退出指令
    133                 cmd_str=cmd.split()#用空格分割 取命令到列表
    134                 #print(cmd_str)
    135                 #print(len(cmd_str))
    136                 if len(cmd_str)==1 and cmd_str[0] in self.cmd_list:#如果是单个命令 并且在命令列表中
    137                 #if len(cmd_str)==1:#如果是单个命令 并且在命令列表中
    138                     if cmd_str[0]==config.HELP:
    139                         self.help()
    140                         continue
    141                     func=getattr(self,'cmd_compr')#调用此方法
    142                     ret=func(cmd_str)
    143                     if ret:
    144                         continue
    145                     else:
    146                         pass
    147                 elif len(cmd_str)>1:
    148                     if hasattr(self,'cmd_%s'%cmd_str[0]):#判断类中是否有此方法
    149                         func=getattr(self,'cmd_%s'%cmd_str[0])#调用此方法
    150                         func(cmd_str)#执行
    151                         continue
    152                 else:
    153                     print('指令出错!')
    154                     self.help()#
    155 
    156     #'''是否要md5'''
    157     def cmd_md5_(self,cmd_list):
    158         '''是否要md5'''
    159         if '--md5' in cmd_list:
    160             return True
    161 
    162     #进度条
    163     def show_pr(self,total):#进度条
    164         received_size = 0 #发送的大小
    165         current_percent = 0 #
    166         while received_size < total:
    167              if int((received_size / total) * 100 )   > current_percent :
    168                   print("#",end="",flush=True)#进度显示
    169                   current_percent = int((received_size / total) * 100 )
    170              new_size = yield #断点跳转 传入的大小
    171              received_size += new_size
    172 
    173     #单个命令
    174     def cmd_compr(self,cmd_str,**kwargs):
    175         mag_dict={
    176                     "action":"compr",
    177                     'actionname':cmd_str[0]
    178                 }
    179         self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送数据
    180         cmd_res_attr=self.get_response()#得到服务器的回复
    181         if type(cmd_res_attr) is not int:#如果不int 类型
    182             if cmd_res_attr["status_code"] ==241:#命令不对
    183                 print(cmd_res_attr['status_msg'])
    184                 return
    185             if cmd_res_attr["status_code"] ==240:#命令不对
    186                 print(cmd_res_attr['status_msg'])
    187                 return
    188         size_l=0#收数据当前大小
    189         self.c.send('准备好接收了,可以发了'.encode('utf-8'))
    190         receive_data= ''.encode()
    191         while size_l< cmd_res_attr:
    192             data=self.c.recv(1024)#开始接收数据
    193             size_l+=len(data)#加上
    194             receive_data += data
    195         else:
    196             receive_data=receive_data.decode()
    197             try:
    198                 receive_data=eval(receive_data)#转为列表 或字典
    199             except Exception as e:
    200                 pass
    201             if type(receive_data) is dict:#如果是字典
    202                 for i in receive_data:
    203                     print(i,receive_data[i])
    204                 return 1
    205             if type(receive_data) is list:#如果是列表
    206                 for i in receive_data:
    207                     print(i)
    208                 return 1
    209             print(receive_data)
    210             return 1
    211 
    212     #切换目录
    213     def cmd_cd(self,cmd_list,**kwargs):
    214         '''切换目录'''
    215         mag_dict={
    216                     "action":"cd",
    217                     'actionname':cmd_list[1]
    218                 }
    219         self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送数据
    220         msg_l=self.c.recv(1024)#接收数据 消息
    221         data=json.loads(msg_l.decode())
    222         if data["status_code"] ==251:#目录不可切换
    223             print(data['status_msg'])
    224             return
    225         elif data["status_code"] ==252:#目录可以换
    226             print(data['status_msg'])
    227             self.c.send(b'1')#发送到服务器,表示可以了
    228             data=self.c.recv(1024)
    229             print(data.decode())
    230             user_dir=data.decode()
    231             print(user_dir)
    232             self.user_dir=user_dir
    233             return
    234         elif data["status_code"] ==256:#目录不存在
    235             print(data['status_msg'])
    236             return
    237 
    238     #删除文件
    239     def cmd_rm(self,cmd_list,**kwargs):
    240         mag_dict={
    241                     "action":"rm",
    242                     'filename':cmd_list[1]
    243                 }
    244         self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息
    245         data=self.get_response()#得到服务器的回复
    246         if data["status_code"] ==245:#文件不存在
    247             print(data['status_msg'])
    248             #print('删除前空间:',data['剩余空间'])
    249             return
    250         elif data["status_code"] ==254:#文件删除完成
    251             print(data['status_msg'])
    252             print('删除前空间:',data['剩余空间'])
    253             pass
    254         self.c.send(b'1')#发送到服务器,表示可以
    255         data=self.get_response()#得到服务器的回复
    256         if data["status_code"] ==255:#文件删除完成
    257             print(data['status_msg'])
    258             print('删除后空间:',data['剩余空间'])
    259             return
    260 
    261     #创建目录
    262     def cmd_mkdir(self,cmd_list,**kwargs):
    263         mag_dict={
    264                     "action":"mkdir",
    265                     'filename':cmd_list[1]
    266                 }
    267         self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息
    268         data=self.get_response()#得到服务器的回复
    269         if data["status_code"] ==257:#目录已经存在
    270             print(data['status_msg'])
    271             return
    272         elif data["status_code"] ==256:#目录创建中
    273             print(data['目录'])
    274             pass
    275         self.c.send(b'1')#发送到服务器,表示可以
    276         data=self.get_response()#得到服务器的回复
    277         if data["status_code"] ==258:#目录创建中完成
    278             print(data['status_msg'])
    279             return
    280         pass
    281 
    282     #删除目录
    283     def cmd_rmdir(self,cmd_list,**kwargs):
    284         mag_dict={
    285                     "action":"rmdir",
    286                     'filename':cmd_list[1]
    287                 }
    288         self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息
    289         data=self.get_response()#得到服务器的回复
    290         if data["status_code"] ==256:#目录不存在
    291             print(data['status_msg'])
    292             return
    293         elif data["status_code"] ==260:#目录不为空
    294             print(data['status_msg'])
    295             print(data['目录'])
    296             return
    297         elif data["status_code"] ==257:#目录删除中
    298             print(data['目录'])
    299             pass
    300         self.c.send(b'1')#发送到服务器,表示可以
    301         data=self.get_response()#得到服务器的回复
    302         if data["status_code"] ==259:#目录删除完成
    303             print(data['status_msg'])
    304             return
    305         pass
    306 
    307     #上传方法
    308     def cmd_put(self,cmd_list,**kwargs):#上传方法
    309         if len(cmd_list) > 1:
    310             filename=cmd_list[1]#取文件名
    311             filename_dir=config.PUT_DIR+filename#拼接文件名路径
    312 
    313             if os.path.isfile(filename_dir):#是否是一个文件
    314                 filesize=os.stat(filename_dir).st_size#获取文件大小
    315                 #执行行为 名字,大小,是否
    316                 mag_dict={
    317                     "action":"put",
    318                     'filename':filename,
    319                     'size':filesize,
    320                     'overridden':True,
    321                     'md5':False
    322                 }
    323                 if self.cmd_md5_(cmd_list):#判断是否进行MD5
    324                     mag_dict['md5'] = True
    325                 self.c.send(json.dumps(mag_dict).encode('utf-8'))#发送文件信息
    326                 data=self.get_response()#得到服务器的回复
    327                 if data["status_code"] ==250:#磁盘空间不足
    328                     print(data['status_msg'])
    329                     print(mag_dict['size'])
    330                     return
    331                 if data["status_code"] ==249:#磁盘空间足够
    332                     print(data['status_msg'])
    333                     print('剩余空间',data['剩余空间'])
    334                     self.c.send(b'1')#发送到服务器,表示可以上传文件了
    335                     data=self.get_response()#得到服务器的回复
    336                     if data["status_code"] ==230:#断点续传
    337                         print(data['status_msg'])
    338                         print(data['文件大小'])
    339                         self.rat=data['文件大小']#文件指针位置
    340                         pass
    341                     elif data["status_code"] ==231:#非断点续传
    342                         print(data['status_msg'])
    343                         self.rat=0#文件指针位置
    344                         pass
    345                     f=open(filename_dir,'rb')#打开文件
    346                     f.seek(self.rat)#移动到位置
    347                     print(mag_dict['md5'])
    348                     self.c.send(b'1')#发送到服务器,表示可以上传文件了
    349                     if mag_dict['md5']==True:
    350                         md5_obj = hashlib.md5()#定义MD5
    351                         progress = self.show_pr(mag_dict['size']) #进度条 传入文件大小
    352                         progress.__next__()
    353                         while self.rat<filesize:
    354                             line=f.read(1024)
    355                             self.c.send(line)
    356                             try:
    357                                 progress.send(len(line))#传入当前数据大小
    358                             except StopIteration as e:
    359                                 print("100%")
    360                                 break
    361                             md5_obj.update(line)#计算MD5
    362 
    363                         else:
    364                             print(filename,'发送完成!')
    365                             f.close()
    366                             md5_val = md5_obj.hexdigest()
    367                             md5_from_server = self.get_response()#服务端的MD5
    368                             if md5_from_server['status_code'] == 248:
    369                                 if md5_from_server['md5'] == md5_val:
    370                                     print("%s 文件一致性校验成功!" % filename)
    371                                     return
    372                     else:
    373                         progress = self.show_pr(mag_dict['size']) #进度条 传入文件大小
    374                         progress.__next__()
    375                         #for line in f:
    376                         while self.rat<filesize:
    377                             line=f.read(1024)
    378                             self.c.send(line)
    379                             try:
    380                                 progress.send(len(line))#传入当前数据大小
    381                             except StopIteration as e:
    382                                 print("100%")
    383                                 break
    384                             #print(line)
    385                         else:
    386                             print(filename,'发送完成!')
    387                             f.close()
    388                             return
    389             else:
    390                 print(filename,'文件不存在!')
    391 
    392     #下载方法
    393     def cmd_get(self,cmd_list,**kwargs):#下载方法
    394         #cmd_split= args[0].split()#指令解析
    395         # if len(cmd_list) == 1:
    396         #     print("没有输入文件名.")
    397         #     return
    398         #down_filename = cmd_list[1].split('/')[-1]#文件名
    399         down_filename=cmd_list[1]#取文件名
    400         file_path='%s/%s'%(config.GET_DIR,down_filename)#拼接文件路径 用户down目录
    401         if os.path.isfile(file_path):#文件是否存
    402             filesize=os.stat(file_path).st_size#获取文件大小
    403             name_down=True
    404         else:
    405             filesize=0
    406             name_down=False
    407         mag_dict={
    408                     "action":"get",
    409                     'filename':cmd_list[1],
    410                     'name_down':name_down,
    411                     'size':filesize
    412                 }
    413         if self.cmd_md5_(cmd_list):#判断是否进行MD5
    414             mag_dict['md5'] = True
    415         self.c.send(json.dumps(mag_dict).encode())#发送
    416         self.c.send(b'1')#发送到服务器,防粘包
    417 
    418         response = self.get_response()#服务器返回文件 的信息
    419         if response["status_code"] ==247:#如文件存在
    420             if name_down==True and response['file_size']==filesize:
    421                 print('文件已经下载完成')
    422                 self.c.send(b'2')
    423                 return
    424             self.c.send(b'1')#发送到服务器,表示可以接收文件了
    425             #if name_down:
    426             received_size = filesize#当前接收的数据大小
    427             #else:
    428             #received_size = 0#当前接收的数据大小
    429 
    430             file_obj = open(file_path,"ab")#打开文件
    431             if self.cmd_md5_(cmd_list):
    432                 md5_obj = hashlib.md5()
    433                 progress = self.show_pr(response['file_size']) #进度条 传入文件大小
    434                 progress.__next__()
    435                 while received_size< response['file_size']:
    436                     if response['file_size'] - received_size>1024:#表示接收不止一次
    437                         size=1024
    438                     else:#最后一次
    439                         size=response['file_size'] - received_size
    440                         #print('最后一个大小',size)
    441                     data= self.c.recv(size)#接收数据
    442 
    443                     try:
    444                         progress.send(len(data))#传入当前数据大小
    445                     except StopIteration as e:
    446                         print("100%")
    447                     received_size+=len(data)#接收数据大小累加
    448                     file_obj.write(data)#写入文件
    449                     md5_obj.update(data)#进行MD5验证
    450                 else:
    451                     print("下载完成".center(60,'-'))
    452                     file_obj.close()
    453                     md5_val = md5_obj.hexdigest()#获取MD5
    454                     #print(md5_val)
    455                     md5_from_server = self.get_response()#服务端的MD5
    456                     #print(md5_from_server['md5'])
    457                     if md5_from_server['status_code'] == 248:
    458                         if md5_from_server['md5'] == md5_val:
    459                             print("%s 文件一致性校验成功!" % down_filename)
    460                     pass
    461             else:
    462                 progress = self.show_pr(response['file_size']) #进度条 传入文件大小
    463                 progress.__next__()
    464                 while received_size< response['file_size']:
    465                     if response['file_size'] - received_size>1024:#表示接收不止一次
    466                         size=1024
    467                     else:#最后一次
    468                         size=response['file_size'] - received_size
    469                         #print('最后一个大小',size)
    470                     data= self.c.recv(size)#接收数据
    471 
    472                     try:
    473                       progress.send(len(data))#传入当前数据大小
    474                     except StopIteration as e:
    475                       print("100%")
    476                     received_size+=len(data)#接收数据大小累加
    477                     file_obj.write(data)#写入文件
    478                     pass
    479 
    480                 else:
    481                     print("下载完成".center(60,'-'))
    482                     file_obj.close()
    483                     pass
    484             self.c.send(b'1')#发送到服务器,表示可以接收文件了
    485 
    486 if __name__=='__main__':
    487 
    488     c=FTPClient()
    489     c.inter()
    View Code
    |           |- - -cfg/#配置目录
    | | |- - -__init__.py
    | | |- - -config.py#配置文件
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 
     5 import os ,sys
     6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     7 sys.path.append(BASE_DIR)#增加环境变量
     8 #print(BASE_DIR)
     9 
    10 PUT_DIR=BASE_DIR+'putfile\'#定义用户上传目录文件路径变量
    11 GET_DIR=BASE_DIR+'down\'#定义用户下载目录文件路径变量
    12 HELP='help'
    13 CMD_LIST=['ls','pwd','info','help']
    View Code
    |- - -ftp_server/#服务端程序目录
    | |- - -__init__.py
    | |- - -bin/#启动目录
    | | |- - -__init__.py
    | | |- - -start.py#服务端视图启动
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 import socket,os,json
     5 import os ,sys
     6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     7 sys.path.append(BASE_DIR)#增加环境变量
     8 
     9 from core import main
    10 
    11 if __name__ == '__main__':
    12 
    13     main.ArvgHandler()
    View Code
    |           |      |- - -user_reg.py#用户注册启动
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 
     5 import configparser
     6 import os ,sys
     7 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     8 sys.path.append(BASE_DIR)#增加环境变量
     9 from cfg import config
    10 #修改个信息 磁盘大小
    11 def set_info(name,pwd,size):
    12     config_info=configparser.ConfigParser()#读数据
    13     config_info.read(config.AUTH_FILE)#读文件 用户名密码
    14     #print(config_info.options(name))
    15     config_info[name]={}
    16     config_info.set(name,config.PWD,pwd)#密码
    17     config_info.set(name,config.QUOTATION,size)#磁盘信息
    18     config_info.write(open(config.AUTH_FILE,'w'))#写入文件
    19     file_path='%s/%s'%(config.USER_HOME,name)#拼接目录路径
    20     os.mkdir(file_path)#创建目录
    21     print('创建完成'.center(60,'='))
    22     print('用户名:[%s]
    密码:[%s]
    磁盘空间:[%s]'%(name,pwd,size))
    23 
    24 if __name__ == '__main__':
    25     name=input('name:')
    26     pwd=input('pwd:')
    27     size=input('size:')
    28     set_info(name,pwd,size)
    View Code
    |           |      |- - -userpwd.cfg#用户信息文件
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 
     5 import os ,sys
     6 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     7 sys.path.append(BASE_DIR)#增加环境变量
     8 #HOME_PATH = os.path.join(BASE_DIR, "home")
     9 
    10 
    11 
    12 #USER_DIR='%s\data\'%BASE_DIR#定义用户数据目录文件路径变量
    13 #USER_DIR='%s/data'%BASE_DIR#定义用户数据目录文件路径变量
    14 #USER_HOME='%s\home\'%BASE_DIR#定义用户家目录文件路径变量
    15 USER_HOME='%s/home'%BASE_DIR#定义用户家目录文件路径变量
    16 #LOG_DIR='%s\log\'%BASE_DIR#日志目录
    17 USER_LOG='%s/log/user_log.log'%BASE_DIR#日志登陆文件
    18 USER_OPERT='%s/log/user_opert.log'%BASE_DIR#日志操作文件
    19 
    20 LOG_LEVEL='DEBUG'#日志级别
    21 
    22 AUTH_FILE='%s/cfg/userpwd.cfg'%BASE_DIR#用户名密码文件
    23 HOST='0.0.0.0'# IP
    24 PORT=9500#端口
    25 QUOTATION='Quotation'#磁盘空间
    26 PWD='PWD'#密码
    View Code
    |           |- - -core/#服务端主要文件目录
    | | |- - -__init__.py
    | | |- - -ftp_server.py#服务端主要逻辑 类
      1 #!usr/bin/env python
      2 #-*-coding:utf-8-*-
      3 # Author calmyan
      4 import socketserver,os,json,pickle,configparser,time
      5 time_format='%Y%m%d%H%M%S'#定义时间格式
      6 times=time.strftime(time_format)#定义时间
      7 
      8 STATUS_CODE={
      9     230:'文件断点继传',
     10     231:'新文件',
     11     240:'格式出错,格式:{"action":"get","filename":"filename","size":100}',
     12     241:'指令错误',
     13     242:'用户名或密码为空',
     14     243:'用户或密码出错',
     15     244:'用户密码通过校验',
     16     245:'文件不存在或不是文件',
     17     246:'服务器上该文件不存在',
     18     247:'准备发送文件,请接收',
     19     248:'md5',
     20     249:'准备接收文件,请上传',
     21     250:'磁盘空间不够',
     22     251:'当前已经为主目录',
     23     252:'目录正在切换',
     24     253:'正在查看路径',
     25     254:'准备删除文件',
     26     255:'删除文件完成',
     27     256:'目录不存在',
     28     257:'目录已经存在',
     29     258:'目录创建完成',
     30     259:'目录删除完成',
     31     260:'目录不是空的',
     32 }
     33 import os ,sys,hashlib
     34 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     35 sys.path.append(BASE_DIR)#增加环境变量
     36 from cfg import config
     37 from core.logs import log_log
     38 from core.logs import user_opert
     39 
     40 
     41 class MyTCPHandler (socketserver.BaseRequestHandler):#
     42 
     43     def setup(self):
     44        print('监听中。。。')
     45     #'''用户名与密码是否为空'''
     46     def cmd_auth(self,*args,**kwargs):#用户校验
     47         '''用户名与密码是否为空'''
     48         data=args[0]#获取 传来的数据
     49         if data.get('username') is None or data.get('password') is None:#如果用户名或密码为空
     50             self.send_mge(242)#发送错误码
     51         name=data.get('username')#用户名
     52         pwd=data.get('password')#密码
     53         print(name,pwd)
     54         user=self.authusername(name,pwd)#用户名与密码的校验 获名用户名
     55         if user is None:#用户名不存在
     56             self.send_mge(243)
     57         else:
     58             self.user=name#保存用户名
     59             self.home_dir='%s/%s'%(config.USER_HOME,self.user)#拼接 用户home目录路径 用户根目录
     60             self.user_home_dir=self.home_dir#当前所在目录
     61             # self.user_dir=self.user_home_dir.split('/')[-1]#当前所在目录 相对
     62             self.dir_join()#进行目录拼接
     63             self.send_mge(244,data={'dir':self.user_dir})#相对 目录
     64 
     65     #目录拼接
     66     def dir_join(self,*args,**kwargs):
     67         self.user_dir=self.user_home_dir.split(self.home_dir)[-1]+'/'#当前所在目录 相对
     68         print(self.user_dir)
     69 
     70     #'''用户名与密码的校验''
     71     def authusername(self,name,pwd):
     72         '''用户名与密码的校验'''
     73         config_info=configparser.ConfigParser()#读数据
     74         config_info.read(config.AUTH_FILE)#读文件 用户名密码
     75         if name in config_info.sections():#用户名存
     76             password=config_info[name]['PWD']
     77             if password==pwd:#密码正确
     78                 print('通过校验!')
     79                 config_info[name]['USERname']=name#名字的新字段
     80                 info_str='用户[%s],成功登陆'%name
     81                 self.log_log.warning(info_str)#记录日志
     82                 #log_log(info_str)
     83                 return config_info[name]
     84             else:
     85                 info_str='用户[%s],登陆错误'%name
     86                 #log_log(info_str)
     87                 self.log_log.warning(info_str)#记录日志
     88                 return 0
     89 
     90     #判断文件 是否存在
     91     def file_name(self,file_path):
     92         if os.path.isfile(file_path):#文件是否存
     93             return  True
     94         else:
     95             return False
     96 
     97     #判断目录是否存在
     98     def file_dir(self,file_path):
     99         if os.path.isdir(file_path):#目录是否存
    100             return  True
    101         else:
    102             return False
    103 
    104     #删除文件
    105     def cmd_rm(self,*args,**kwargs):
    106         cmd_dict=args[0]#获取字典
    107         action=cmd_dict["action"]
    108         filename =cmd_dict['filename']#文件名
    109         file_path='%s/%s'%(self.user_home_dir,filename)#拼接文件路径
    110         if not self.file_name(file_path):
    111             self.send_mge(245)#文件不存在
    112             return
    113         else:
    114             user_size=self.disk_size()#获取磁盘信息
    115             self.send_mge(254,data={'剩余空间':user_size})#准备删除文件
    116             file_size=os.path.getsize(file_path)#获取文件大小
    117             pass
    118         self.request.recv(1) #客户端确认 防粘包
    119         os.remove(file_path)
    120         new_size=float((float(user_size)+float(file_size))/1024000)#空间大小增加
    121         self.set_info(str(new_size))#传入新大小
    122         self.send_mge(255,data={'剩余空间':new_size})#删除文件完成
    123         info_str=self.log_str('删除文件')#生成日志信息
    124         self.user_opert.critical(info_str)#记录日志
    125         return
    126 
    127     #创建目录
    128     def cmd_mkdir(self,*args,**kwargs):
    129         cmd_dict=args[0]#获取字典
    130         action=cmd_dict["action"]
    131         filename =cmd_dict['filename']#目录名
    132         file_path='%s/%s'%(self.user_home_dir,filename)#拼接目录路径
    133         if self.file_dir(file_path):
    134             self.send_mge(257)#目录已经 存在
    135             return
    136         else:
    137             self.send_mge(256,data={'目录':'创建中...'})#目录创建中
    138             self.request.recv(1) #客户端确认 防粘包
    139             os.mkdir(file_path)#创建目录
    140             self.send_mge(258)#目录完成
    141             info_str=self.log_str('创建目录')#生成日志信息
    142             self.user_opert.critical(info_str)#记录日志
    143             return
    144 
    145     #删除目录
    146     def cmd_rmdir(self,*args,**kwargs):
    147         cmd_dict=args[0]#获取字典
    148         action=cmd_dict["action"]
    149         filename =cmd_dict['filename']#目录名
    150         file_path='%s/%s'%(self.user_home_dir,filename)#拼接目录路径
    151         if not self.file_dir(file_path):
    152             self.send_mge(256)#目录不存在
    153             return
    154         elif os.listdir(file_path):
    155             self.send_mge(260,data={'目录':'无法删除'})#目录不是空的
    156             return
    157         else:
    158             self.send_mge(257,data={'目录':'删除中...'})#目录创建中
    159             self.request.recv(1) #客户端确认 防粘包
    160             os.rmdir(file_path)#删除目录
    161             self.send_mge(259)#目录删除完成
    162             info_str=self.log_str('删除目录')#生成日志信息
    163             self.user_opert.critical(info_str)#记录日志
    164             return
    165 
    166     #磁盘空间大小
    167     def disk_size(self):
    168         attr_list=self.user_info()#调用个人信息
    169         put_size=attr_list[1]#取得磁盘信息
    170         user_size=float(put_size)*1024000#字节
    171         return user_size
    172 
    173     #'''客户端上传文件 '''
    174     def cmd_put(self,*args,**kwargs):
    175         '''客户端上传文件 '''
    176         cmd_dict=args[0]#获取字典
    177         filename =cmd_dict['filename']#文件名
    178         file_size= cmd_dict['size']#文件大小
    179         #user_home_dir='%s/%s'%(config.USER_HOME,self.user)#拼接 用户home目录路径
    180         file_path='%s/%s'%(self.user_home_dir,filename)#拼接文件路径
    181         user_size=self.disk_size()#取得磁盘信息
    182         if float(file_size)>float(user_size):#空间不足
    183             self.send_mge(250,data={'剩余空间':user_size})
    184             return
    185         self.send_mge(249,data={'剩余空间':user_size})#发送一个确认
    186         self.request.recv(1) #客户端确认 防粘包
    187         if self.file_name(file_path):#判断文件名是否存在,
    188             s_file_size=os.path.getsize(file_path)##获取服务器上的文件大小
    189             if file_size>s_file_size:#如果服务器上的文件小于要上传的文件进
    190                 tmp_file_size=os.stat(file_path).st_size#计算临时文件大小
    191                 reversed_size=tmp_file_size#接收到数据大小
    192                 self.send_mge(230,data={'文件大小':reversed_size})#发送临时文件大小
    193                 pass
    194             else:# file_size==s_file_size:#如果大小一样
    195                 file_path=file_path+'_'+times#命名新的文件 名
    196                 reversed_size=0#接收到数据大小
    197                 self.send_mge(231)#发送 不是断点文件
    198                 pass
    199         else:
    200             reversed_size=0#接收到数据大小
    201             self.send_mge(231)#发送 不是断点文件
    202             pass
    203 
    204         f=open(file_path,'ab')
    205         self.request.recv(1) #客户端确认 防粘包
    206         if cmd_dict['md5']:#是否有 md5
    207             md5_obj = hashlib.md5() #   进行MD5
    208             while reversed_size< int(file_size):#接收小于文件 大小
    209                 if int(file_size) - reversed_size>1024:#表示接收不止一次
    210                     size=1024
    211                 else:#最后一次
    212                     size=int(file_size) - reversed_size
    213                     #print('最后一个大小',size)
    214                 data= self.request.recv(size)#接收数据
    215                 md5_obj.update(data)
    216                 reversed_size+=len(data)#接收数据大小累加
    217                 f.write(data)#写入文件
    218             else:
    219                 f.close()
    220                 print('[%s]文件上传完毕'.center(60,'-')%filename)
    221                 md5_val = md5_obj.hexdigest()#得出MD5
    222                 print(md5_val)
    223                 self.send_mge(248,{'md5':md5_val})#发送md5给客户端
    224         else:
    225             while reversed_size< int(file_size):#接收小于文件 大小
    226                 if int(file_size) - reversed_size>1024:#表示接收不止一次
    227                     size=1024
    228                 else:#最后一次
    229                     size=int(file_size) - reversed_size
    230                     #print('最后一个大小',size)
    231                 data= self.request.recv(size)#接收数据
    232                 reversed_size+=len(data)#接收数据大小累加
    233                 f.write(data)#写入文件
    234             else:
    235                 print('[%s]文件上传完毕'%filename.center(60,'-'))
    236                 f.close()
    237         new_size=float((float(user_size)-float(file_size))/1024000)#扣除空间大小
    238         self.set_info(str(new_size))#传入新大小
    239         info_str=self.log_str('文件上传')#生成日志信息
    240         self.user_opert.critical(info_str)#记录日志
    241         return
    242 
    243     #用户下载文件
    244     def cmd_get(self,*args,**kwargs):#用户下载文件
    245         ''' 用户下载文件'''
    246         data=args[0]
    247         print(data)
    248         if data.get('filename') is None:#判断文件名不为空
    249             self.send_mge(245)
    250             return
    251 
    252         self.request.recv(1) #客户端确认 防粘包
    253         file_path='%s/%s'%(self.user_home_dir,data.get('filename'))#拼接文件路径 用户文件路径
    254         if os.path.isfile(file_path):#判断文件是否存在
    255             file_obj=open(file_path,'rb')#打开文件句柄
    256             file_size=os.path.getsize(file_path)#获取文件大小
    257             if data['name_down']:
    258                 send_size=data['size']#已经发送数据大小
    259                 #self.send_mge(230,data={'文件大小':file_size})#断点续传
    260             else:
    261                 send_size=0
    262                 #self.send_mge(231)#非断点续传
    263             #self.request.recv(1) #客户端确认 防粘包
    264             file_obj.seek(send_size)#移动到
    265             self.send_mge(247,data={'file_size':file_size})#发送相关信息
    266             attr=self.request.recv(1024) #客户端确认 防粘包
    267             if attr.decode()=='2':return #如果返回是
    268             if data.get('md5'):
    269                 md5_obj = hashlib.md5()
    270                 while send_size<file_size:
    271                     line=file_obj.read(1024)
    272                 #for line in file_obj:
    273                     self.request.send(line)
    274                     md5_obj.update(line)
    275                 else:
    276                     file_obj.close()
    277                     md5_val = md5_obj.hexdigest()
    278                     self.send_mge(248,{'md5':md5_val})
    279                     print("发送完毕.")
    280             else:
    281                 while send_size<file_size:
    282                     line=file_obj.read(1024)
    283                 #for line in file_obj:
    284                     self.request.send(line)
    285                 else:
    286                     file_obj.close()
    287                     print("发送完毕.")
    288             self.request.recv(1) #客户端确认 防粘包
    289             info_str=self.log_str('下载文件')#生成日志信息
    290             #user_opert(info_str)#记录日志
    291             self.user_opert.critical(info_str)#记录日志
    292             return
    293 
    294     #切换目录
    295     def cmd_cd(self,cmd_dict,*args,**kwargs):
    296         '''切换目录'''
    297         cmd_attr=cmd_dict['actionname']#获取命令
    298         if cmd_attr=='..' or cmd_attr=='../..':
    299             if (self.home_dir)==self.user_home_dir:
    300                 self.send_mge(251)
    301                 return
    302             elif cmd_attr=='../..':
    303                 self.send_mge(252)#可以切换到上级目录
    304                 self.user_home_dir=self.home_dir#绝对目录 = home
    305                 self.user_dir='/'
    306                 clinet_ack=self.request.recv(1024)#为了去粘包
    307                 self.request.send(self.user_dir.encode())#返回相对目录
    308                 return
    309             else:
    310                 self.send_mge(252)#可以切换到上级目录
    311                 print(self.user_home_dir)#绝对目录
    312                 print(os.path.dirname(self.user_home_dir))#父级目录
    313                 self.user_home_dir=os.path.dirname(self.user_home_dir)#父级目录
    314                 self.dir_join()#目录拼接切换
    315                 clinet_ack=self.request.recv(1024)#为了去粘包
    316                 self.request.send(self.user_dir.encode())#返回相对目录
    317                 return
    318 
    319         elif os.path.isdir(self.user_home_dir+'/'+cmd_attr):#如果目录存在
    320             self.send_mge(252)
    321             self.user_home_dir=self.user_home_dir+'/'+cmd_attr#目录拼接
    322             self.dir_join()#相对目录拼接切换
    323             clinet_ack=self.request.recv(1024)#为了去粘包
    324             print(clinet_ack.decode())
    325             self.request.send(self.user_dir.encode())
    326             return
    327         else:
    328             self.send_mge(256)#目录不存在
    329             return
    330 
    331     #查看目录路径 CD
    332     def cmd_pwd(self,cmd_dict):
    333         self.request.send(str(len(self.user_dir.encode('utf-8'))).encode('utf-8'))#发送大小
    334         clinet_ack=self.request.recv(1024)#为了去粘包
    335         self.request.send(self.user_dir.encode())#发送相对路径
    336         info_str=self.log_str('查看目录路径')#生成日志信息
    337         #logger.warning
    338         self.user_opert.critical(info_str)#记录日志
    339         return
    340 
    341     #修改个信息 磁盘大小
    342     def set_info(self,new_size):
    343         config_info=configparser.ConfigParser()#读数据
    344         config_info.read(config.AUTH_FILE)#读文件 用户名密码
    345         print(config_info.options(self.user))
    346         config_info.set(self.user,config.QUOTATION,new_size)
    347         config_info.write(open(config.AUTH_FILE,'w'))
    348 
    349     #读取个人信息
    350     def user_info(self):
    351         config_info=configparser.ConfigParser()#读数据
    352         config_info.read(config.AUTH_FILE)#读文件 用户名密码
    353         print(config_info.options(self.user))
    354         pwds=config_info[self.user][config.PWD]#密码
    355         Quotation=config_info[self.user][config.QUOTATION]#磁盘配额 剩余
    356         user_info={}
    357         user_info['用户名']=self.user
    358         user_info['密码']=pwds
    359         user_info['剩余磁盘配额']=Quotation
    360         return user_info,Quotation
    361 
    362     #查看用户信息
    363     def cmd_info(self,*args,**kwargs):
    364         attr=self.user_info()
    365         info_dict=attr[0]
    366         self.request.send(str(len(json.dumps(info_dict))).encode('utf-8'))#
    367         clinet_ack=self.request.recv(1024)#为了去粘包
    368         self.request.send(json.dumps(info_dict).encode('utf-8'))#发送指令
    369         info_str=self.log_str('查看用户信息')#生成日志信息
    370         self.user_opert.critical(info_str)#记录日志
    371         return
    372 
    373     #日志信息生成
    374     def log_str(self,msg,**kwargs):
    375         info_str='用户[%s]进行了[%s]操作'%(self.user,msg)
    376         return info_str
    377 
    378 
    379     #目录查看
    380     def cmd_ls(self,*args,**kwargs):
    381         data=os.listdir(self.user_home_dir)#查看目录文件
    382         print(data)
    383         datas=json.dumps(data)#转成json格式
    384         self.request.send(str(len(datas.encode('utf-8'))).encode('utf-8'))#发送大小
    385         clinet_ack=self.request.recv(1024)#为了去粘包
    386         self.request.send(datas.encode('utf-8'))#发送指令
    387         info_str=self.log_str('目录查看')#生成日志信息
    388         self.user_opert.critical(info_str)#记录日志
    389         return
    390     ##单个命令
    391     def cmd_compr(self,cmd_dict,**kwargs):
    392         attr=cmd_dict['actionname']#赋于变量
    393         if hasattr(self,'cmd_%s'%attr):#是否存在
    394             func=getattr(self,'cmd_%s'%attr)#调用
    395             func(cmd_dict)
    396             return
    397         else:
    398             print('没有相关命令!')
    399             self.send_mge(241)
    400             return
    401 
    402     #'''发送信息码给客户端'''
    403     def send_mge(self,status_code,data=None):
    404         '''发送信息码给客户端'''
    405         mge={'status_code':status_code,'status_msg':STATUS_CODE[status_code]}#消息
    406         if data:#不为空
    407             mge.update(data)#提示码进行更新
    408         print(mge)
    409         self.request.send(json.dumps(mge).encode())#发送给客户端
    410 
    411     #重写handle方法
    412     def handle(self):#重写handle方法
    413         while True:
    414             #try:
    415             self.data=self.request.recv(1024).strip()#接收数据
    416             print('ip:{}'.format(self.client_address[0]))#连接的ip
    417             print(self.data)
    418             self.log_log=log_log()#登陆日志
    419             self.user_opert=user_opert()#操作日志
    420             if not self.data:
    421                 print("[%s]客户端断开了!."%self.user)
    422                 info_str='用户[%s],退出'%self.user
    423 
    424                 break
    425             cmd_dict=json.loads(self.data.decode())#接收 数据
    426             if cmd_dict.get('action') is not None:#判断数据格式正确
    427                 action=cmd_dict['action']#文件 头
    428                 if hasattr(self,'cmd_%s'%action):#是否存在
    429                     func=getattr(self,'cmd_%s'%action)#调用
    430                     func(cmd_dict)
    431                 else:
    432                     print('没有相关命令!')
    433                     self.send_mge(241)
    434             else:
    435                 print('数据出错!')
    436                 self.send_mge(240)
    437             #except Exception as e:
    438              #  print('客户端断开了!',e)
    439               # break
    View Code
    |           |      |- - -logs.py#日志主要逻辑 类
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 import os,logging,time
     5 from cfg import config
     6 LOG_LEVEL=config.LOG_LEVEL
     7 
     8 
     9 def log_log():#登陆日志,传入内容
    10     logger=logging.getLogger('用户成功登陆日志')#设置日志模块
    11     logger.setLevel(logging.DEBUG)
    12     fh=logging.FileHandler(config.USER_LOG,encoding='utf-8')#写入文件
    13     fh.setLevel(config.LOG_LEVEL)#写入信息的级别
    14     fh_format=logging.Formatter('%(asctime)s %(message)s',datefmt='%m/%d/%Y %I:%M:%S %p')#日志格式
    15     fh.setFormatter(fh_format)#关联格式
    16     logger.addHandler(fh)#添加日志输出模式
    17     #logger.warning(info_str)
    18     return logger
    19 
    20 def user_opert():#用户操作日志,传入内容
    21     logger=logging.getLogger('用户操作日志')#设置日志模块
    22     logger.setLevel(logging.CRITICAL)
    23     fh=logging.FileHandler(config.USER_OPERT,encoding='utf-8')#写入文件
    24     fh.setLevel(config.LOG_LEVEL)#写入信息的级别
    25     fh_format=logging.Formatter('%(asctime)s %(message)s',datefmt='%m/%d/%Y %I:%M:%S %p')#日志格式
    26     fh.setFormatter(fh_format)#关联格式
    27     logger.addHandler(fh)#添加日志输出模式
    28     #logger.critical(info_str)
    29     return logger
    View Code
    |           |      |- - -main.py#服务端启动主程序
     1 #!usr/bin/env python
     2 #-*-coding:utf-8-*-
     3 # Author calmyan
     4 
     5 import socketserver,os,json,pickle
     6 import os ,sys
     7 BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))#获取相对路径转为绝对路径赋于变量
     8 sys.path.append(BASE_DIR)#增加环境变量
     9 from cfg import config
    10 
    11 
    12 from  core.ftp_server import  MyTCPHandler
    13 
    14 import optparse
    15 class ArvgHandler(object):
    16     def __init__(self):#   可  传入系统参数
    17         self.paresr=optparse.OptionParser()#启用模块
    18         #self.paresr.add_option('-s','--host',dest='host',help='服务绑定地址')
    19         #self.paresr.add_option('-s','--port',dest='host',help='服务端口')
    20         (options,args)=self.paresr.parse_args()#返回一个字典与列表的元组
    21 
    22         self.verufy_args(options,args)#进行校验
    23     def verufy_args(self,options,args):
    24         '''校验与调用'''
    25         if hasattr(self,args[0]):#反射判断参数
    26             func=getattr(self,args[0])#生成一个实例
    27             func()#开始调用
    28         else:
    29             self.paresr.print_help()#打印帮助文档
    30     def start(self):
    31         print('服务启动中....')
    32         s=socketserver.ThreadingTCPServer((config.HOST,config.PORT),MyTCPHandler)#实例化一个服务端对象
    33         s.serve_forever()#运行服务器
    34         print('服务关闭')
    View Code


    
    

    您的资助是我最大的动力!
    金额随意,欢迎来赏!

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐按钮。
    如果,您希望更容易地发现我的新博客,不妨点击一下绿色通道的关注我

    如果,想给予我更多的鼓励,求打

    因为,我的写作热情也离不开您的肯定支持,感谢您的阅读,我是【莫柔落切】!

    联系或打赏博主【莫柔落切】!https://home.cnblogs.com/u/uge3/

  • 相关阅读:
    常用正则表达式
    The Skins Factory 界面设计欣赏
    The Regulator 轻松上手
    Visual C#的Web XML编程
    业务流程不是需求
    如使用ODBC连接informix
    AJAX在信息系统中的应用研究
    浅谈几个SQL的日志概念
    量产 朗科(Netac)朗盛系列闪存盘E108 8G 手记
    【转】告诉大家他们是怎么成为富翁的
  • 原文地址:https://www.cnblogs.com/uge3/p/7048938.html
Copyright © 2011-2022 走看看