python实现简单FTP
-
需求
1. 用户登陆 ---configparse 存储信息 进行登录验证 2. 上传/下载文件 ---get、put函数 3. 不同用户家目录不同 ----configparse定义家目录 4. 查看当前目录下文件 ----dir命令,如何进行权限判定,只能进入到自己的目录下面? 5. 充分使用面向对象知识 -----就是用类、函数
-
代码结构
-
服务端
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 # @Time : 2017/12/14 15:25 4 # @Author : lichuan 5 # @File : 服务端.py 6 7 8 import socketserver 9 import struct 10 import json 11 import subprocess 12 import os 13 import configparser 14 from module import common 15 import subprocess 16 17 class My_FtpServer(socketserver.BaseRequestHandler): 18 19 def handle(self): 20 # while True: 21 try: 22 name = self.name_check() #进行用户名验证 23 self.path = common.get_home_path(name) #根据用户名获取家目录 24 if not os.path.exists(self.path): 25 os.mkdir(self.path) 26 # os.chdir(path) 27 print(os.getcwd()) 28 while True: 29 data=self.request.recv(8) #接收命令长度 30 if not data:break #防止在linux、mac等系统中会有问题 31 msg_length=struct.unpack('q',data)[0] #是一个元组形势的数据,包含命令长度 32 cmd = self.request.recv(msg_length).decode('utf-8') 33 if cmd.upper() == 'QUIT': #quit 进行退出 34 print('break') 35 break 36 if hasattr(self,cmd.split()[0]): #收到的消息,进行切分,拿出第一个进行判断 37 print(cmd.split()[0]) 38 func = getattr(self,cmd.split()[0]) 39 func(cmd) 40 else: #如果没有函数,则进行执行命令 41 os.chdir(self.path) 42 res=subprocess.Popen(cmd, 43 shell=True, 44 stdout=subprocess.PIPE, 45 stderr=subprocess.PIPE) 46 err = res.stderr.read() 47 if err: 48 back_msg=err 49 else: 50 back_msg=res.stdout.read() 51 if cmd.split()[0] == 'cd': 52 new_path = os.getcwd()+os.sep+cmd.split()[1] 53 if self.path in new_path: 54 print('self.path',self.path) 55 print('new_path',new_path) 56 self.path = new_path 57 print(self.path) 58 self.send_msg(back_msg) 59 except Exception as e: 60 print("Error:",e) 61 print('handle exception!') 62 63 def send_header(self,file_info): #发送报头 64 # file_info = {'cmd': cmd,'status': status} 65 f_head = json.dumps(file_info) #json化代码,用于传输 66 f_head_bytes = bytes(f_head, encoding='utf-8') #变成bytes类型 67 self.request.send(struct.pack('q', len(f_head_bytes))) # 传报头长度过去 68 self.request.sendall(f_head_bytes) # 传报头过去 69 70 def get(self,cmd): 71 print('----get function----') 72 # print(os.getcwd()) 73 os.chdir(self.path) 74 l = cmd.split() 75 if len(l) != 2: 76 print('get command is worng!') 77 return 78 # BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 79 f = l[1] 80 if os.path.exists(f): 81 file_path = os.path.abspath(f) 82 else: 83 print('file not exists!') 84 return 85 print('file path:',os.path.abspath(file_path)) 86 if os.path.isfile(file_path) and self.path in file_path: 87 file_size = os.path.getsize(file_path) 88 file_md5 = common.GetFileMd5(file_path) # 获取文件的md5值 89 status = 'OK' 90 file_info = {'cmd': cmd, 'filename': os.path.basename(file_path), 'file_size': file_size, 'md5': file_md5,'status':status} 91 # f_head = json.dumps(file_info) 92 # print(type(f_head)) 93 # f_head_bytes = bytes(f_head, encoding='utf-8') 94 # print(f_head_bytes,type(f_head_bytes),type(f_head)) 95 # print('len:',len(f_head)) 96 # self.request.send(struct.pack('q', len(f_head_bytes))) # 传报头长度过去 97 # self.request.sendall(f_head_bytes) # 传报头过去 98 self.send_header(file_info) 99 with open(file_path, 'rb') as read_f: 100 for line in read_f: 101 self.request.send(line) # 传文件过去 102 else: 103 print('权限不够或者不是文件') 104 status = 'ERROR' 105 file_info = {'cmd': cmd,'status': status} 106 self.send_header(file_info) 107 108 109 def put(self,cmd): 110 print('----put function----') 111 os.chdir(self.path) 112 l = cmd.split() 113 if len(l) != 2: 114 print('put command is worng!') 115 return 116 head_l=self.request.recv(8) #接收报头长度 117 longth = int(struct.unpack('q',head_l)[0]) #接收到的应该是个元组,取第一位,即长度 118 file_json = self.request.recv(longth).decode('utf-8') 119 head_dic = json.loads(file_json) 120 status = head_dic['status'] 121 if status == 'OK': 122 file_name=head_dic['filename'] 123 file_size=head_dic['file_size'] 124 recv_size = 0 125 with open(file_name,'wb') as write_f: 126 while recv_size < file_size: 127 res=self.request.recv(1024) 128 recv_size+=len(res) 129 write_f.write(res) 130 print(recv_size,file_size) 131 file_path=os.path.abspath(file_name) 132 # print('file_name:',file_name) 133 print('file_path:',file_path) 134 file_md5=common.GetFileMd5(file_path) 135 # print(file_md5) 136 # print(head_dic['md5']) 137 if file_md5 == head_dic['md5']: 138 print('文件传输成功') 139 else: 140 print('文件不一致') 141 os.remove(file_path) 142 143 def send_msg(self,msg):#用于发送信息 144 self.head_l = struct.pack('q',len(msg)) 145 self.request.send(self.head_l) 146 self.request.sendall(msg) 147 148 #进行用户名密码验证的函数 149 def name_check(self): 150 while True: 151 name = self.request.recv(1024).decode('utf-8') 152 self.request.send('0'.encode('utf-8')) 153 passwd=self.request.recv(1024).decode('utf-8') 154 if common.login(name,passwd): 155 self.request.send('0'.encode('utf-8')) 156 return name 157 else: 158 self.request.send('1'.encode('utf-8')) 159 continue 160 161 #执行dir命令显示目录下的内容 162 def show_path_content(self,path): 163 print('show path'+path) 164 cmd = 'dir ' + path 165 res1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 166 return res1.stdout.read().decode('gbk') 167 168 if __name__ == '__main__': 169 fserver = socketserver.ThreadingTCPServer(('127.0.0.1',8001),My_FtpServer) 170 fserver.serve_forever() 171 fserver.server_close() 172 # for l in os.listdir(os.getcwd()): 173 # print(l) 174 # for i in os.walk(os.path.dirname(os.path.dirname((os.path.abspath(__file__))))): 175 # print(i)
-
客户端
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 # @Time : 2017/12/14 15:25 4 # @Author : lichuan 5 # @File : 客户端.py 6 7 8 #_*_coding:utf-8_*_ 9 10 import time 11 import socket 12 import struct 13 import json 14 import os 15 from module import common 16 17 BUFSIZE=1024 18 19 20 class FTP_ClientServer: 21 def __init__(self,ip_port,logined=False): 22 self.ip_port=ip_port 23 self.logined = logined 24 self.socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 25 try: 26 self.socket.connect(ip_port) 27 except: 28 self.socket.close() 29 raise 30 31 def run(self): 32 while True: 33 if not self.logined: 34 self.login() 35 else: 36 msg = input('>>:').strip() 37 msg_longth=struct.pack('q',len(msg)) 38 self.socket.send(msg_longth) 39 self.socket.sendall(msg.encode('utf-8')) 40 if msg.upper() == 'QUIT': 41 break 42 if hasattr(self,msg.split()[0]): 43 func=getattr(self,msg.split()[0]) 44 func(msg) 45 else: #传送命令过去的逻辑 46 recv = self.socket.recv(8) 47 data = struct.unpack('q',recv) #返回的是一个元组,(123,),123是长度 48 print(data) 49 longth = data[0] 50 recv_size = 0 51 recv_data = b'' 52 while recv_size < longth: 53 recv_data+=self.socket.recv(1024) 54 recv_size+=1024 55 print(recv_data.decode('gbk')) 56 57 #程序开始时,进行登录验证的函数 58 def login(self): 59 name = input('username:').strip() 60 passwd = input('password:').strip() 61 if len(name) == 0: return False 62 if len(passwd) == 0: return False 63 self.socket.send(name.encode('utf-8')) # 发送name过去进行验证 64 recv = self.socket.recv(1) 65 if recv.decode('utf-8') != '0': # name返回结果不为0,说明name不存在 66 print('name error') 67 return False 68 self.socket.send(passwd.encode('utf-8')) 69 recv = self.socket.recv(1) 70 if recv.decode('utf-8') != '0': # passwd返回结果不为0,说明passwd错误 71 print('passwd error') 72 return False 73 print('login successful!') 74 self.logined = True 75 return True 76 77 def recv_msg(self): 78 head_l = self.socket.recv(8) 79 data_head = struct.unpack('q',head_l) 80 81 def get(self,cmd): 82 l = cmd.split() 83 if len(l) != 2: 84 print('get command is worng!') 85 return 86 head_l = self.socket.recv(8) # 接收报头长度 87 longth = int(struct.unpack('q', head_l)[0]) # 接收到的应该是个元组,取第一位,即长度 88 file_json = self.socket.recv(longth).decode('utf-8') 89 head_dic = json.loads(file_json) 90 status = head_dic['status'] 91 if status == 'OK': 92 file_name = head_dic['filename'] 93 file_size = head_dic['file_size'] 94 recv_size = 0 95 with open(file_name, 'wb') as write_f: 96 while recv_size < file_size: 97 res = self.socket.recv(1024) 98 recv_size += len(res) 99 write_f.write(res) 100 print(recv_size, file_size) 101 file_path = os.path.abspath(file_name) 102 # print('file_name:',file_name) 103 print('file_path:', file_path) 104 file_md5 = common.GetFileMd5(file_path) 105 # print(file_md5) 106 # print(head_dic['md5']) 107 if file_md5 == head_dic['md5']: 108 print('文件传输成功') 109 else: 110 print('文件不一致') 111 os.remove(file_path) 112 else: 113 print('权限不够或者不是文件') 114 115 def put(self,msg): 116 l=msg.split() 117 if len(l) != 2: 118 print('put command is worng!') 119 return 120 BASE_DIR=os.path.dirname(os.path.abspath(__file__)) 121 f=l[1] 122 if os.path.exists(f): 123 file_path = os.path.abspath(f) 124 else: 125 print('file not exists!') 126 return 127 if os.path.isfile(file_path): 128 file_size = os.path.getsize(file_path) 129 file_md5 = common.GetFileMd5(file_path) #获取文件的md5值 130 status = 'OK' 131 file_info = {'cmd':msg,'filename':os.path.basename(file_path),'file_size':file_size,'md5':file_md5,'status':status} 132 self.send_header(file_info) 133 # f_head=json.dumps(file_info) 134 # print(type(f_head)) 135 # f_head_bytes = bytes(f_head,encoding='utf-8') 136 # print(f_head_bytes,type(f_head_bytes),type(f_head)) 137 # print('len:',len(f_head)) 138 # self.socket.send(struct.pack('q',len(f_head_bytes))) #传报头长度过去 139 # self.socket.sendall(f_head_bytes) #传报头过去 140 num=0 141 with open(file_path,'rb') as read_f: 142 for line in read_f: 143 self.socket.send(line) #传文件过去 144 # num+=1 145 # print(num) 146 else: 147 status = 'ERROR' 148 file_info = {'cmd': msg,'status': status} 149 self.send_header(file_info) 150 151 def send_header(self,file_info): #发送报头 152 # file_info = {'cmd': cmd,'status': status} 153 f_head = json.dumps(file_info) #json化代码,用于传输 154 f_head_bytes = bytes(f_head, encoding='utf-8') #变成bytes类型 155 self.socket.send(struct.pack('q', len(f_head_bytes))) # 传报头长度过去 156 self.socket.sendall(f_head_bytes) # 传报头过去 157 158 if __name__ == '__main__': 159 ip_port = ('127.0.0.1', 8001) 160 f=FTP_ClientServer(ip_port) 161 f.run()
-
用户定义文件
1 [egon] 2 name = egon 3 home_path = egon 4 passwd = bc5b9cb3e4ab483335edab3347f3c102 5 6 [alex] 7 name = alex 8 home_path = alex 9 passwd = bc5b9cb3e4ab483335edab3347f3c102
-
公共定义函数文件
1 #!/usr/bin/env python 2 #-*- coding:utf-8 -*- 3 # @Time : 2017/10/20 15:46 4 # @Author : lichuan 5 # @File : common.py 6 7 import hashlib 8 import os 9 import re 10 from log import my_log_settings 11 import logging 12 import pickle 13 import configparser 14 15 def login(name,passwd): 16 path = os.path.dirname(os.path.abspath(__file__))+os.sep+'users.cfg' 17 config = configparser.ConfigParser() 18 config.read(path) 19 # print(config.sections()) 20 if name not in config.sections(): 21 print('name is wrong!') 22 return False 23 encrypt_passwd = encrypt(passwd) 24 # print(encrypt_passwd) 25 p = config.get(name,'passwd') 26 # print(p) 27 if encrypt_passwd == p: 28 print('login successful') 29 return True 30 else: 31 print('password is wrong!') 32 return False 33 34 #根据name获取家目录路径 35 def get_home_path(name): 36 config = configparser.ConfigParser() 37 path = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'users.cfg' 38 # print(path) 39 config.read(path) 40 # print(config.sections()) 41 if name not in config.sections(): 42 print('name is not exist') 43 return None 44 else: 45 p = config.get(name,'home_path') 46 new_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.sep +'db'+os.sep+p 47 # print(new_path) 48 return new_path 49 50 def encrypt(str): 51 ''' 52 对传入字符串进行加盐加密 53 :param str: 需要进行加密的字符串 54 :return: 返回加密过的字符串 55 ''' 56 encrpt=hashlib.md5() 57 encrpt.update(bytes('admin1234nnnnnn',encoding='utf-8')) 58 encrpt.update(bytes(str,encoding='utf-8')) 59 return encrpt.hexdigest() 60 61 #计算文件的md5值 62 def GetFileMd5(filename): 63 if not os.path.isfile(filename): 64 print('file is not exists') 65 return 66 myhash = hashlib.md5() 67 with open(filename,'rb') as f: 68 while True: 69 b = f.read(8096) 70 if not b : 71 break 72 myhash.update(b) 73 return myhash.hexdigest() 74 75 # if __name__ == '__main__': 76 # a=GetFileMd5('users.cfg') 77 # print(a)
-
执行效果
C:\Python35\python.exe C:/Users/dell/PycharmProjects/day7_2/bin2/客户端2.py username:alex password:123456 login successful! >>:dir (458,) 驱动器 C 中的卷没有标签。 卷的序列号是 D48A-2DD8 C:\Users\dell\PycharmProjects\day7_2\db\alex 的目录 2018/01/09 11:02 <DIR> . 2018/01/09 11:02 <DIR> .. 2018/01/05 17:18 5,231 aa.txt 2018/01/09 11:02 5,231 bb.txt 2018/01/08 16:46 <DIR> ddd 2018/01/05 14:43 8,815 test.py 3 个文件 19,277 字节 3 个目录 23,366,094,848 可用字节 >>:rm bb.txt (61,) 'rm' 不是内部或外部命令,也不是可运行的程序 或批处理文件。 >>:del bb.txt (0,) >>:dir (414,) 驱动器 C 中的卷没有标签。 卷的序列号是 D48A-2DD8 C:\Users\dell\PycharmProjects\day7_2\db\alex 的目录 2018/01/09 11:15 <DIR> . 2018/01/09 11:15 <DIR> .. 2018/01/05 17:18 5,231 aa.txt 2018/01/08 16:46 <DIR> ddd 2018/01/05 14:43 8,815 test.py 2 个文件 14,046 字节 3 个目录 23,366,037,504 可用字节 >>:put bb.txt >>:get aa.txt 1024 5231 1546 5231 1605 5231 1646 5231 1698 5231 1700 5231 1752 5231 1774 5231 1817 5231 1862 5231 1903 5231 1946 5231 2026 5231 2062 5231 2152 5231 2185 5231 2211 5231 2261 5231 2297 5231 2388 5231 2423 5231 2449 5231 2485 5231 2514 5231 2535 5231 2537 5231 2562 5231 2600 5231 2647 5231 2649 5231 2673 5231 2698 5231 2723 5231 2767 5231 2787 5231 2847 5231 2961 5231 3023 5231 3065 5231 3107 5231 3150 5231 3173 5231 3221 5231 3263 5231 3309 5231 3348 5231 3384 5231 3429 5231 3477 5231 3518 5231 3558 5231 3607 5231 3634 5231 3668 5231 3709 5231 3750 5231 3765 5231 3803 5231 3837 5231 3839 5231 3863 5231 3886 5231 3911 5231 3955 5231 3975 5231 4036 5231 4083 5231 4127 5231 4142 5231 4181 5231 4201 5231 4240 5231 4292 5231 4369 5231 4482 5231 4524 5231 4559 5231 4618 5231 4685 5231 4726 5231 4815 5231 4883 5231 4902 5231 4952 5231 4989 5231 5056 5231 5084 5231 5116 5231 5118 5231 5146 5231 5181 5231 5214 5231 5227 5231 5229 5231 5231 5231 file_path: C:\Users\dell\PycharmProjects\day7_2\bin2\aa.txt 文件传输成功 >>:quit
定义了一个self.path参数,对用户的目录进行限定,以此做权限控制。