好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

高级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                         continue141                     func=getattr(self,'cmd_compr')#调用此方法142                     ret=func(cmd_str)143                     if ret:144                         continue145                     else:146                         pass147                 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                         continue152                 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 True161 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_size172 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                 return185             if cmd_res_attr["status_code"] ==240:#命令不对186                 print(cmd_res_attr['status_msg'])187                 return188         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 += data195         else:196             receive_data=receive_data.decode()197             try:198                 receive_data=eval(receive_data)#转为列表 或字典199             except Exception as e:200                 pass201             if type(receive_data) is dict:#如果是字典202                 for i in receive_data:203                     print(i,receive_data[i])204                 return 1205             if type(receive_data) is list:#如果是列表206                 for i in receive_data:207                     print(i)208                 return 1209             print(receive_data)210             return 1211 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             return225         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_dir233             return234         elif data["status_code"] ==256:#目录不存在235             print(data['status_msg'])236             return237 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             return250         elif data["status_code"] ==254:#文件删除完成251             print(data['status_msg'])252             print('删除前空间:',data['剩余空间'])253             pass254         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             return260 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             return272         elif data["status_code"] ==256:#目录创建中273             print(data['目录'])274             pass275         self.c.send(b'1')#发送到服务器,表示可以276         data=self.get_response()#得到服务器的回复277         if data["status_code"] ==258:#目录创建中完成278             print(data['status_msg'])279             return280         pass281 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             return293         elif data["status_code"] ==260:#目录不为空294             print(data['status_msg'])295             print(data['目录'])296             return297         elif data["status_code"] ==257:#目录删除中298             print(data['目录'])299             pass300         self.c.send(b'1')#发送到服务器,表示可以301         data=self.get_response()#得到服务器的回复302         if data["status_code"] ==259:#目录删除完成303             print(data['status_msg'])304             return305         pass306 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':False322                 }323                 if self.cmd_md5_(cmd_list):#判断是否进行MD5324                     mag_dict['md5'] = True325                 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                     return331                 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                         pass341                     elif data["status_code"] ==231:#非断点续传342                         print(data['status_msg'])343                         self.rat=0#文件指针位置344                         pass345                     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()#定义MD5351                         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                                 break361                             md5_obj.update(line)#计算MD5362 363                         else:364                             print(filename,'发送完成!')365                             f.close()366                             md5_val = md5_obj.hexdigest()367                             md5_from_server = self.get_response()#服务端的MD5368                             if md5_from_server['status_code'] == 248:369                                 if md5_from_server['md5'] == md5_val:370                                     print("%s 文件一致性校验成功!" % filename)371                                     return372                     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                                 break384                             #print(line)385                         else:386                             print(filename,'发送完成!')387                             f.close()388                             return389             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         #     return398         #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=True404         else:405             filesize=0406             name_down=False407         mag_dict={408                     "action":"get",409                     'filename':cmd_list[1],410                     'name_down':name_down,411                     'size':filesize412                 }413         if self.cmd_md5_(cmd_list):#判断是否进行MD5414             mag_dict['md5'] = True415         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                 return424             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=1024438                     else:#最后一次439                         size=response['file_size'] - received_size440                         #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()#获取MD5454                     #print(md5_val)455                     md5_from_server = self.get_response()#服务端的MD5456                     #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                     pass461             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=1024467                     else:#最后一次468                         size=response['file_size'] - received_size469                         #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                     pass479 480                 else:481                     print("下载完成".center(60,'-'))482                     file_obj.close()483                     pass484             self.c.send(b'1')#发送到服务器,表示可以接收文件了485 486 if __name__=='__main__':487 488     c=FTPClient()489     c.inter() 

查看更多关于高级FTP服务器使用介绍的详细内容...

  阅读:37次