-
需求
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參數,對用戶的目錄進行限定,以此做權限控制。