高级FTP


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



6. **测试:**  

----------

用户登陆

                1.登录
                2.注册
        
(输入编号,任意键退出:)1

请输入你的用户名(q返回):a

请输入你的密码:a

登陆成功!

(help查看命令)help

        ls              显示当前目录
        cd ../..        切换目录
        get filename    下载文件
        put filename    上传文件
        mkdir dirname   创建目录
        qouta           查看剩余可用磁盘配额

----------

7. **备注:**  

1.程序运行在client/bin/startclient.py和server/bin/startserver.py,请先运行server端,在运行client端

2.模拟的本地目录为client/db目录,下面有测试文件abcd

3.模拟的服务器用户家目录为db/home/$name目录

4.测试账号用户名密码为a,a,测试目录为a,下面有测试文件abc

server端主文件为:

  1 #!/usr/bin/env python
  2 # coding=utf-8
  3 #Author:yang
  4 import socketserver,hashlib
  5 import json,os,sys
  6 from conf  import settings
  7 
  8 class MyTCPHandler(socketserver.BaseRequestHandler):
  9 
 10     user = None
 11     home_dir = None
 12     auth_dir = None
 13     total_space = None
 14     free_space = None
 15 
 16     def handle(self):
 17         while True:
 18             try:
 19                 self.data = self.request.recv(1024).strip()
 20                 print("{} wrote:".format(self.client_address[0]))
 21                 print(self.data)
 22                 cmd_dic = json.loads(self.data.decode())
 23                 action = cmd_dic["action"]
 24                 if hasattr(self,action):
 25                     func = getattr(self,action)
 26                     func(cmd_dic)
 27 
 28             except ConnectionResetError as e:
 29                 print("err",e)
 30                 break
 31 
 32     def auth(self,*args):
 33         #验证登录
 34         cmd_dic = args[0]
 35         username = cmd_dic['username']
 36         password = cmd_dic['password']
 37         auth = os.path.join(settings.USER_AUTH,username)
 38         if os.path.exists(auth):
 39             with open(auth, 'r') as f:
 40                 user_dict  = json.load(f)
 41             print(user_dict)
 42             if user_dict['password'] == password:
 43                 self.user = username
 44                 self.home_dir = os.path.join(settings.USER_HOME,username)
 45                 self.current_dir = self.home_dir
 46                 self.total_space = user_dict['quota']
 47 
 48                 ret = []
 49                 size = 0
 50                 for root, dirs, files in os.walk(self.home_dir):
 51                     for files_path in files:
 52                         ret.append(os.path.join(root, files_path))
 53                 for i in ret:
 54                     if os.path.isfile(i):
 55                         size += os.path.getsize(i)
 56                         print(size)
 57                 self.free_space = int(int(self.total_space) - size)
 58                 if not os.path.exists(self.home_dir):
 59                     os.mkdir(self.home_dir)
 60                 self.request.sendall(bytes("yes", encoding='utf8'))
 61             else:
 62                 self.request.sendall(bytes("no", encoding='utf8'))
 63         else:
 64             self.request.sendall(bytes("reg", encoding='utf8'))
 65 
 66     def reg(self,*args):
 67         #注册
 68         cmd_dic = args[0]
 69         username = cmd_dic['username']
 70         password = cmd_dic['password']
 71         quota = cmd_dic['quota']
 72         auth = os.path.join(settings.USER_AUTH, username)
 73         if os.path.exists(auth):
 74             self.request.sendall(bytes("no", encoding='utf8'))
 75         else:
 76             self.user = username
 77             self.home_dir = os.path.join(settings.USER_HOME, username)
 78             self.current_dir = self.home_dir
 79             self.total_space = quota
 80             os.mkdir(self.home_dir)
 81             data = {
 82                 "username": username,
 83                 "password": password,
 84                 "quota": quota
 85             }
 86             with open(auth, 'w') as f:
 87                 json.dump(data,f)
 88             self.request.sendall('yes'.encode('utf-8'))
 89 
 90     def ls(self,*args):
 91         #查看目录
 92         msg = os.popen('dir'+ ' '+self.current_dir).read()
 93         self.request.sendall(msg.encode('utf-8'))
 94 
 95     def mkdir(self,*args):
 96         #创建目录
 97         cmd_dic = args[0]
 98         dir = cmd_dic['filename']
 99         dirname = os.path.join(self.current_dir,dir)
100         if not os.path.isdir(dirname):
101             os.popen('mkdir' + ' ' + dirname)
102             self.request.sendall('yes'.encode('utf-8'))
103         else:
104             self.request.sendall('no'.encode('utf-8'))
105 
106     def cd(self,*args):
107         #切换目录
108         cmd_dic = args[0]
109         dir = cmd_dic['filename']
110         if dir == ' 'or dir == '..':
111             self.current_dir = self.home_dir
112             self.request.sendall('yes'.encode('utf-8'))
113         else:
114             dirname = os.path.join(self.current_dir, dir)
115             if os.path.isdir(dirname):
116                 self.current_dir = dirname
117                 self.request.sendall('yes'.encode('utf-8'))
118 
119             else:
120                 self.request.sendall('no'.encode('utf-8'))
121 
122     def put(self,*args):
123         '''接收客户端文件'''
124         cmd_dic = args[0]
125         file = cmd_dic["filename"]
126         filesize = cmd_dic["size"]
127         filename = os.path.join(self.current_dir, file)
128         file_swap = filename + '.swap'
129         if not os.path.isfile(file_swap):
130             self.request.send('normal'.encode("utf-8"))
131             if filesize > self.free_space:
132                 self.request.sendall('no'.encode('utf-8'))
133             else:
134                 self.free_space = self.free_space - filesize
135                 f = open(file_swap , "wb")
136                 m = hashlib.md5()
137                 self.request.send("ok".encode('utf-8'))
138                 received_size = 0
139                 while received_size < filesize:
140                     data = self.request.recv(4096)
141                     f.write(data)
142                     m.update(data)
143                     received_size += len(data)
144                     percent = int(received_size / filesize * 100)
145                     tag = "#" * int(percent / 2)
146                     sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
147                     if percent == 100:
148                         print('\n')
149                 else:
150                     new_file_md5 = m.hexdigest()
151                     server_file_md5 = self.request.recv(4096)
152                     if new_file_md5 == server_file_md5.decode():
153                         f.close()
154                         os.rename(file_swap,filename)
155                         print('文件下载完毕!')
156                     else:
157                         print("文件不一致!")
158                         f.close()
159         else:
160             self.request.send('swap'.encode("utf-8"))
161             swap_file_size = 0
162             with open(file_swap,'r') as f:
163                 m = hashlib.md5()
164                 for line in f:
165                     m.update(line)
166                     swap_file_size += len(line)
167             swap_file_md5 = m.hexdigest()
168             file_data = {
169                 'swap_size':swap_file_size,
170                 'swap_md5': swap_file_md5
171             }
172             self.request.sendall(json.dumps(file_data).encode('utf-8'))
173             if self.request.recv(4096).decode() == 'yes':
174                 # 断点续传开始
175                 if filesize > self.free_space:
176                     self.request.sendall('no'.encode('utf-8'))
177                 else:
178                     self.free_space = self.free_space - filesize
179                     received_size = swap_file_size
180                     while received_size < filesize:
181                         f = open(file_swap, "ab")
182                         data = self.request.recv(4096)
183                         f.write(data)
184                         received_size += len(data)
185                         percent = int(received_size / filesize * 100)
186                         tag = "#" * int(percent / 2)
187                         sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
188                         if percent == 100:
189                             print('\n')
190                     else:
191                         with open(file_swap, 'r') as f:
192                             m = hashlib.md5()
193                             for line in f:
194                                 m.update(line)
195                         new_file_md5 = m.hexdigest()
196                         server_file_md5 = self.request.recv(4096)
197                         if new_file_md5 == server_file_md5.decode():
198                             f.close()
199                             print('文件下载完毕!')
200                             os.rename(file_swap, filename)
201                         else:
202                             print("文件不一致!")
203                             f.close()
204             else:
205                 os.remove(file_swap)
206 
207 
208 
209 
210 
211 
212 
213     def get(self,*args):
214         '''发送给客户端文件'''
215         cmd_dic = args[0]
216         file = cmd_dic["filename"]
217         filename = os.path.join(self.current_dir,file)
218         server_response = self.request.recv(4096)
219         if server_response.decode() == 'normal':
220             if os.path.isfile(filename):
221                 filesize = os.stat(filename).st_size
222                 self.request.send(str(filesize).encode('utf-8'))
223                 # 防止粘包,等服务器确认
224                 print('filesize')
225                 server_response = self.request.recv(4096)
226                 print(server_response.decode())
227                 f = open(filename, "rb")
228                 m = hashlib.md5()
229                 for line in f:
230                     m.update(line)
231                     self.request.send(line)
232                 else:
233                     print("接收成功")
234                     self.request.send(m.hexdigest().encode('utf-8'))
235                     f.close()
236             else:
237                 self.request.sendall('no'.encode('utf-8'))
238         if server_response.decode() == 'swap':
239             #断点续传
240             if server_response.decode() == 'swap':
241                 file_data = json.loads(self.request.recv(4096).decode())
242 
243                 with open(filename, "rb") as f:
244                     received_size = 0
245                     m = hashlib.md5()
246                     for line in f:
247                         m.update(line)
248                         received_size += len(line)
249                         if received_size == file_data['swap_size']:
250                             if m.hexdigest() == file_data['swap_md5']:
251                                 self.request.send('yes'.encode('utf-8'))
252                                 break
253                             else:
254                                 print('文件已经损坏,删除文件')
255                                 self.request.send('no'.encode('utf-8'))
256                                 break
257                 server_response = self.request.recv(4096)
258                 if server_response.decode() == 'no':
259                     print('文件大小超出限额')
260                 else:
261                     f = open(filename, "rb")
262                     f.seek(file_data['swap_size'])
263                     received_size = file_data['swap_size']
264                     for line in f:
265                         self.request.send(line)
266                         received_size += len(line)
267                         percent = int(received_size / filesize * 100)
268                         tag = "#" * int(percent / 2)
269                         sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
270                         if percent == 100:
271                             print('\n')
272                         else:
273                             print('传输被中断')
274                     else:
275                         print("发送成功")
276                         with open(filename, 'rb') as f:
277                             m = hashlib.md5()
278                             for line in f:
279                                 m.update(line)
280                         self.request.send(m.hexdigest().encode('utf-8'))
281                         f.close()
282 
283     def free(self,*args):
284         self.request.sendall(str(self.free_space).encode('utf-8'))
285 
286 
287 
288 def run():
289     server = socketserver.ThreadingTCPServer((settings.HOST, settings.PORT), MyTCPHandler)
290     server.serve_forever()
server.py

server端配置文件为:

 1 #!/usr/bin/env python
 2 # coding=utf-8
 3 #Author:yang
 4 import  os
 5 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 6 BASE_DIR_DB = os.path.join(BASE_DIR,'db')
 7 USER_HOME = os.path.join(BASE_DIR_DB,'home')
 8 USER_AUTH = os.path.join(BASE_DIR_DB,'auth')
 9 CLIENT_HOME = os.path.join(BASE_DIR_DB,'client')
10 
11 HOST = 'localhost'
12 PORT = 9998
settings.py

client端主文件为:

  1 #!/usr/bin/env python
  2 # coding=utf-8
  3 #Author:yang
  4 
  5 import socket
  6 import json
  7 import os,hashlib,sys
  8 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  9 
 10 class FtpClient(object):
 11     #client类
 12 
 13 
 14     def __init__(self):
 15         self.client = socket.socket()
 16         self.dir = os.path.join(BASE_DIR,'db')
 17 
 18     def help(self):
 19         msg = '''
 20         ls              显示当前目录
 21         cd ../..        切换目录
 22         get filename    下载文件
 23         put filename    上传文件
 24         mkdir dirname   创建目录
 25         qouta           查看剩余可用磁盘配额
 26         '''
 27         print(msg)
 28     def connect(self,ip,port):
 29         self.client.connect((ip, port))
 30 
 31     def interactive(self):
 32         #交互界面
 33         #self.authenticate()
 34         while True:
 35             cmd = input(">>(help查看命令)").strip()
 36             if len(cmd) ==0:continue
 37             cmd_str = cmd.split()[0]
 38             if hasattr(self,"cmd_%s" % cmd_str):
 39                 func = getattr(self,"cmd_%s" % cmd_str)
 40                 func(cmd)
 41             else:
 42                 self.help()
 43 
 44     def cmd_qouta(self,*args):
 45         #查询配额
 46         msg_dic = {
 47             "action": "free"
 48         }
 49         self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
 50         msg = self.client.recv(4096)
 51         print(msg.decode())
 52 
 53     def cmd_ls(self,*args):
 54         #查看当前目录下文件
 55         msg_dic = {
 56             "action": "ls"
 57         }
 58         self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
 59         msg = self.client.recv(4096)
 60         print(msg.decode())
 61 
 62     def cmd_mkdir(self,*args):
 63         #创建目录
 64         cmd_split = args[0].split()
 65         msg_dic = {
 66             "action": "mkdir",
 67             'filename':cmd_split[1]
 68         }
 69         self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
 70         msg = self.client.recv(4096)
 71         if msg.decode() == 'yes':
 72             print('创建成功')
 73         else:
 74             print('目录已经存在')
 75 
 76     def cmd_cd(self, *args):
 77         #切换目录
 78         cmd_split = args[0].split()
 79         msg_dic = {
 80             "action": "cd",
 81             'filename': cmd_split[1]
 82         }
 83         self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
 84         msg = self.client.recv(4096)
 85         if msg.decode() == 'yes':
 86             print('切换成功')
 87         else:
 88             print('目录不存在')
 89 
 90     def cmd_login(self):
 91         #登录
 92         while True:
 93             name = input("请输入你的用户名(q返回):")
 94             if name.lower().strip() == 'q':
 95                 print('返回上一层成功!')
 96                 break
 97             else:
 98                 password = input("请输入你的密码:")
 99                 m = hashlib.md5()
100                 m.update(password.encode('utf-8'))
101                 psw = m.hexdigest()
102                 msg_dic = {
103                     "action": "auth",
104                     "username": name,
105                     "password": psw,
106                 }
107                 self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
108                 msg = self.client.recv(4096)
109                 if msg.decode() == 'yes':
110                     print("登陆成功!")
111                     self.interactive()
112                 elif msg.decode() == 'no':
113                     print("密码错误!")
114                 elif msg.decode() == 'reg':
115                     print('用户不存在,请注册!')
116                 else:
117                     print("未知错误!")
118 
119     def cmd_reg(self):
120         #注册
121         while True:
122             name = input("请输入你需要注册的用户名(q返回):")
123             if name.lower().strip() == 'q':
124                 print('返回上一层成功!')
125                 break
126             else:
127                 password = input("请输入你要注册的密码:")
128                 quota = input("请输入你要申请的磁盘配额():")
129                 m = hashlib.md5()
130                 m.update(password.encode('utf-8'))
131                 psw = m.hexdigest()
132                 msg_dic = {
133                     "action": "reg",
134                     "username": name,
135                     "password": psw,
136                     "quota":quota
137                 }
138                 self.client.sendall(json.dumps(msg_dic).encode("utf-8"))
139                 msg = self.client.recv(4096)
140                 if msg.decode() == 'yes':
141                     print("注册成功!")
142                     self.interactive()
143                 elif msg.decode() == 'no':
144                     print("用户名已经存在!")
145                 else:
146                     print("未知错误!")
147 
148     def cmd_put(self,*args):
149         #上传文件
150         cmd_split =  args[0].split()
151         if len(cmd_split) >1:
152             file = cmd_split[1]
153             filename = os.path.join(self.dir,cmd_split[1])
154             if os.path.isfile(filename):
155                 filesize = os.stat(filename).st_size
156                 msg_dic = {
157                     "action": "put",
158                     "filename":file,
159                     "size": filesize,
160                 }
161                 self.client.send( json.dumps(msg_dic).encode("utf-8")  )
162                 print("send",json.dumps(msg_dic).encode("utf-8") )
163                 server_response = self.client.recv(4096)
164                 if server_response.decode() == 'normal':
165                     server_response = self.client.recv(4096)
166                     if server_response.decode() == 'no':
167                         print('文件大小超出限额')
168                     else:
169                         f = open(filename,"rb")
170                         m = hashlib.md5()
171                         received_size = 0
172                         for line in f:
173                             m.update(line)
174                             self.client.send(line)
175                             len(line)
176                             received_size += len(line)
177                             percent = int(received_size / filesize * 100)
178                             tag = "#" * int(percent / 2)
179                             sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
180                             if percent == 100:
181                                 print('\n')
182                         else:
183                             print("发送成功")
184                             self.client.send(m.hexdigest().encode('utf-8'))
185                             f.close()
186                 #断点续传
187                 if server_response.decode() == 'swap':
188                     file_data = json.loads(self.client.recv(4096).decode())
189 
190                     with open(filename, "rb") as f:
191                         received_size = 0
192                         m = hashlib.md5()
193                         for line in f:
194                             m.update(line)
195                             received_size += len(line)
196                             if received_size == file_data['swap_size']:
197                                 if m.hexdigest() == file_data['swap_md5']:
198                                     self.client.send('yes'.encode('utf-8'))
199                                     break
200                                 else:
201                                     print('文件已经损坏,删除文件')
202                                     self.client.send('no'.encode('utf-8'))
203                                     break
204                     server_response = self.client.recv(4096)
205                     if server_response.decode() == 'no':
206                         print('文件大小超出限额')
207                     else:
208                         f = open(filename, "rb")
209                         f.seek(file_data['swap_size'])
210                         received_size = file_data['swap_size']
211                         for line in f:
212                             self.client.send(line)
213                             received_size += len(line)
214                             percent = int(received_size / filesize * 100)
215                             tag = "#" * int(percent / 2)
216                             sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
217                             if percent == 100:
218                                 print('\n')
219                         else:
220                             print("发送成功")
221                             with open(filename, 'rb') as f:
222                                 m = hashlib.md5()
223                                 for line in f:
224                                     m.update(line)
225                             self.client.send(m.hexdigest().encode('utf-8'))
226                             f.close()
227             else:
228                 print(file,"文件不存在")
229 
230     def cmd_get(self,*args):
231         #下载文件
232         cmd_split = args[0].split()
233         msg_dic = {
234             "action": "get",
235             "filename": cmd_split[1],
236         }
237 
238         self.client.send(json.dumps(msg_dic).encode("utf-8"))
239         filename = os.path.join(self.dir, cmd_split[1])
240         file_swap = filename + '.swap'
241         if not os.path.isfile(file_swap):
242             self.client.send('normal'.encode("utf-8"))
243             filesize = self.client.recv(4096)
244             if filesize.isdigit() == False:
245                 print('文件不存在')
246             else:
247                 filesize = int(filesize)
248                 f = open(file_swap, "wb")
249                 m = hashlib.md5()
250                 self.client.send("ok".encode('utf-8'))
251                 received_size = 0
252                 while received_size < filesize:
253                     data = self.client.recv(4096)
254                     f.write(data)
255                     m.update(data)
256                     received_size += len(data)
257                     percent = int(received_size / filesize * 100)
258                     tag = "#" * int(percent / 2)
259                     sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
260                     if percent == 100:
261                         print('\n')
262                 else:
263                     new_file_md5 = m.hexdigest()
264                     server_file_md5 = self.client.recv(4096)
265                     if new_file_md5 == server_file_md5.decode():
266                         f.close()
267                         os.rename(file_swap,filename)
268                         print('文件下载完毕!')
269                     else:
270                         print("文件不一致!")
271                         f.close()
272         else:
273             #断点续传
274             self.client.send('swap'.encode("utf-8"))
275             swap_file_size = 0
276             with open(file_swap, 'rb') as f:
277                 m = hashlib.md5()
278                 for line in f:
279                     m.update(line)
280                     swap_file_size += len(line)
281             swap_file_md5 = m.hexdigest()
282             file_data = {
283                 'swap_size': swap_file_size,
284                 'swap_md5': swap_file_md5
285             }
286             self.client.sendall(json.dumps(file_data).encode('utf-8'))
287             if self.client.recv(4096).decode() == 'yes':
288                 # 断点续传开始
289                 filesize = int(self.client.recv(4096).decode())
290                 received_size = swap_file_size
291                 while received_size < filesize:
292                     f = open(file_swap, "ab")
293                     data = self.client.recv(4096)
294                     f.write(data)
295                     received_size += len(data)
296                     percent = int(received_size / filesize * 100)
297                     tag = "#" * int(percent / 2)
298                     sys.stdout.write("[%s/%s]%s>%s\r" % (filesize, received_size, tag, percent))
299                     if percent == 100:
300                         print('\n')
301                 else:
302                     with open(file_swap, 'r') as f:
303                         m = hashlib.md5()
304                         for line in f:
305                             m.update(line)
306                     new_file_md5 = m.hexdigest()
307                     server_file_md5 = self.client.recv(4096)
308                     if new_file_md5 == server_file_md5.decode():
309                         os.rename(file_swap, filename)
310                         print('文件下载完毕!')
311                     else:
312                         print("文件不一致!")
313                     f.close()
314             else:
315                 os.remove(file_swap)
316                 print('文件损坏')
317 
318 def run():
319     ftp = FtpClient()
320     #实例化
321     ftp.connect("localhost",9998)
322     #链接server端
323     while True:
324         menu = '''
325                 1.登录
326                 2.注册
327         '''
328         print(menu)
329         choice = input('>>(输入编号,任意键退出:)')
330         if choice == '1':
331             ftp.cmd_login()
332         if choice == '2':
333             ftp.cmd_reg()
334         else:
335             exit('退出成功!')
client.py

 



     

posted @ 2017-07-09 16:20  咫灵  阅读(441)  评论(0)    收藏  举报