〖Demo〗-- 高级FTP服务器开发

【高级FTP服务器开发】

要求:

1. 用户加密认证

2. 多用户同时登陆

3. 每个用户有自己的家目录且只能访问自己的家目录

4. 对用户进行磁盘配额、不同用户配额可不同

5. 用户可以登陆server后,可切换目录

6. 查看当前目录下文件

7. 上传下载文件,保证文件一致性

8. 传输过程中现实进度条

9. 支持断点续传

 

 

 

 

路径如下

  1 import socket
  2 import pickle
  3 import hashlib
  4 import sys
  5 import time
  6 import os
  7 A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
  8 class Ftp_client(object):
  9     def __init__(self):
 10         self.client = socket.socket()
 11     def help(self):
 12         '''
 13         帮助说明
 14         :return:
 15         '''
 16         print('''请输入正确的指令:
 17         ls: 查看根目录下文件
 18         cd: 切换目录
 19         download: 下载文件
 20         upload:上文件
 21         mkdir:新建立文件夹
 22                 ''')
 23     def connet(self, ip, port):
 24         '''
 25         链接服务器
 26         :param ip:
 27         :param port:
 28         :return:
 29         '''
 30         self.client.connect((ip, port))
 31         data = self.client.recv(1024)
 32         print(data.decode())
 33         self.main()
 34         self.ftp_main()
 35     def login(self):
 36         '''
 37         登录
 38         :return:
 39         '''
 40         name = input('请输入姓名').lower()
 41         password = input('请输入密码')
 42         dict = {'name': name, 'password': password}
 43         self.client.sendall(pickle.dumps(dict))
 44         data = self.client.recv(1024)
 45         print(data.decode())
 46         if data.decode()=='输入有误':
 47             return False
 48     def register(self):
 49         '''
 50         注册
 51         :return:
 52         '''
 53         while True:
 54             a = input('请输入注册哪种用户: 1: 普通用户(可用空间10M), 2: VIP用户(可用30M)')
 55             if a =='1':
 56                 space = 10485760
 57                 break
 58             elif a== '2':
 59                 space = 31457280
 60                 break
 61             else:
 62                 print('输入有误')
 63                 continue
 64         name = input('请输入姓名').lower()
 65         pd = input('请输入密码')
 66         dict = {'name': name, 'password': pd, 'space': space}
 67         self.client.sendall(pickle.dumps(dict))
 68         data = self.client.recv(1024)
 69         print(data.decode())
 70         if data.decode()== '用户名已存在,请重新输入':
 71             return False
 72 
 73     def main(self):
 74         while True:
 75             a = input('请输入 1. 用户登录 2. 用户注册 3.退出')
 76             if a == '1':
 77                 self.client.sendall('login'.encode())
 78                 res = self.login()
 79             elif a == '2':
 80                 self.client.sendall('register'.encode())
 81                 res = self.register()
 82             elif a == '3':
 83                 exit()
 84             else:
 85                 print('输入有误')
 86                 continue
 87             if res is False:
 88                     continue
 89             else:
 90                 break
 91 
 92     def download(self):
 93         '''
 94         下载
 95         :return:
 96         '''
 97         while True:
 98             data = self.client.recv(1024)
 99             choose = input('文件所在路径 1 根目录 2 子目录')
100             if choose == '1':
101                 path = '1'
102                 break
103             elif choose =='2':
104                 path = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入
105                 break
106             else:
107                 print('输入有误')
108                 continue
109         self.client.sendall(path.encode())
110         data = self.client.recv(1024)
111         filename = input('请输入下载文件名')
112         self.client.sendall(filename.encode())
113         size = self.client.recv(1024).decode()
114         if size == '该文件不存在':
115             print ('该文件不存在')
116             return False
117         else:
118             size = int(size)
119             if os.path.exists(os.path.join(A, 'db', filename)):
120                 r_size = int(os.path.getsize(os.path.join(A, 'db', filename)))#存在文件的size
121                 while True:
122                     choose = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载')
123                     if choose == '2':
124                         dic={}
125                         dic['choose'] = choose
126                         self.client.sendall(pickle.dumps(dic))
127                         return False
128                     elif choose == '1':
129                         f = open(os.path.join(A, 'db',filename),'wb')
130                         r_size = 0
131                         break
132                     elif choose == '3':
133                         name = input('请输入新文件名')
134                         f = open(os.path.join(A, 'db',name),'wb')
135                         r_size = 0
136                         break
137                     elif choose == '4':
138                         f = open(os.path.join(A, 'db',filename),'ab')
139                         break
140                     else:
141                         print('输入有误,请重新输入')
142                 dic={}
143                 dic['choose'] = choose
144                 dic['size'] = r_size
145                 self.client.sendall(pickle.dumps(dic))
146             else:
147                 r_size = 0
148                 f = open(os.path.join(A, 'db', filename),'xb')
149 
150 
151             if size == 0:
152                 f.close()
153                 print('接收完成')
154             else:
155                 while  r_size < size:
156                     file = self.client.recv(1024)
157                     f.write(file)
158                     f.flush() #文件强行写入file,预防中途中断
159                     r_size += len(file)
160                     view_bar(r_size, size)
161                     time.sleep(0.1)
162                 else:
163                     print('接收完成')
164                     f.close()
165 
166 
167 
168     def upload(self):
169         filename = input('请输入上传的文件名')
170         if os.path.exists(os.path.join(A, 'db', filename)):
171             size = os.path.getsize(os.path.join(A, 'db', filename)) #文件size
172             path = input('请输入上传的路径,子路径用 / 分隔隔开, h为根目录')
173             dic = {}
174             dic['filename'] = filename
175             dic['size'] = size
176             dic['path'] = path
177             self.client.sendall(pickle.dumps(dic))
178             f =  open(os.path.join(A, 'db', filename), 'rb')
179         else:
180             print ('此文件不存在')
181             return False
182         data = self.client.recv(1024)
183         ls = pickle.loads(data) #ls[2]: ;
184         if ls[-1]=='1': #ls[-1]:检查下载的路径是否存在
185             print ('此路径不存在')
186             return False
187         if ls[0] == '0':#ls[0]:检查空间够不够;
188             print ('空间不足,不能上传')
189 
190         else:
191             if ls[1] == '0': #ls[1]:检查下载是否存在
192                 while True:
193                     a = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载')
194                     f_ls = []
195                     f_ls.append(a)
196                     if a == '1':
197                         break
198                     elif a == '2':
199                         return False
200                     elif a =='3':
201                         f_name = input('请输入新文件名')
202                         f_ls.append(f_name)
203                         break
204                     elif a=='4':
205                         l_size = ls[2] #ls[2]:已下载的文件大小
206                         f.seek(l_size) #把指针放到已下载的地方,继续下载
207                         break
208                     else:
209                         print ('输入有误')
210             else:
211                 f_ls = []
212                 f_ls.append('5') # 5:下载文件不存在
213             self.client.sendall(pickle.dumps(f_ls))
214             data = self.client.recv(1024).decode()
215             print (data)
216             for line in f:
217                 self.client.sendall(line)
218                 num = f.tell() #查看文件上传位置
219                 view_bar(num, size)
220                 time.sleep(0.1)
221             f.close()
222             print ('接收完成')
223             return False
224 
225 
226     def ls(self):
227         data = self.client.recv(1024)
228         if data =='0'.encode():
229             print('此目录为空')
230         elif data =='1'.encode():
231             print('此文件不存在')
232         else:
233             ls = pickle.loads(data)
234             print('此文件里有:')
235             for i in ls:
236                 print(i)
237 
238     def mkdir(self):
239         name = input('请输入文件夹名')
240         self.client.sendall(name.encode())
241         data = self.client.recv(1024).decode()
242         print(data)
243 
244     def cd(self):
245         name = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入
246         self.client.sendall(name.encode())
247         path = self.client.recv(1024).decode()
248 
249         if path == '0':
250             print ('此目录不存在')
251             return False
252         else:
253             print ('所在文件夹路径为 %s' % path)
254         self.client.sendall('ok'.encode())
255         data = self.client.recv(1024)
256         if data =='0'.encode():
257             print('此目录为空')
258         else:
259             ls = pickle.loads(data)
260             print('此文件里有:')
261             for i in ls:
262                 print(i)
263 
264 
265 
266     def ftp_main(self):
267         while True:
268             a = input('请输入相应的指令, help:查询, exit:退出')
269             if hasattr(self, a):
270                 self.client.sendall(a.encode())
271                 func = getattr(self, a)
272                 func()
273             elif a == 'exit':
274                 exit()
275             else:
276                 self.help()
277                 continue
278 
279 
280 
281 
282 
283 
284 def view_bar(num, total):
285     '''进度条'''
286     rate = float(num) / float(total)
287     rate_num = int(rate * 100)
288     r = '\r%d%%' % (rate_num, ) #\r 回到到开头
289     sys.stdout.write(r)
290     sys.stdout.flush()  #删除记录
291 
292 ftp = Ftp_client()
293 ftp.connet('localhost', 9999)
client
import socketserver
import pickle
import os
import time
A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                self.request.sendall('链接成功'.encode())
                while True:
                    data = self.request.recv(1024).decode()
                    if data =='login':
                        a = self.login()
                    else:
                        a = self.register() #a为 list,[0]是name,[1]是可用空间
                    if a is False:
                        continue
                    else:
                        while True:
                            data = self.request.recv(1024)
                            func = getattr(self, data.decode())
                            func(a)
            except Exception: #检查客户端是否连接正常
                print('客户端断开')
                break

    def login(self):
        db_dict = pickle.load(open(os.path.join(A, 'db', 'register'),'rb'))
        self.data = self.request.recv(1024)
        dict = pickle.loads(self.data)
        if dict['name'] in db_dict:
            if dict['password']==db_dict[dict['name']][0]:
                self.request.sendall('登录成功'.encode())
                return [dict['name'], db_dict[dict['name']][1]] #返回用户名,用户可用空间
            else:
                self.request.sendall('输入有误'.encode())
                return False
        else:
            self.request.sendall('输入有误'.encode())
            return False
    def register(self):
        dict = pickle.loads(self.request.recv(1024))
        print (dict)
        if os.path.exists(os.path.join(A, 'db', dict['name'])):
            self.request.sendall('用户名已存在,请重新输入'.encode())
            return False
        else:
            self.request.sendall('注册成功'.encode())
            os.makedirs(os.path.join(A, 'db', dict['name']))
            # n_dict ={}
            n_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb'))
            print (1)
            print (n_dict)
            n_dict[dict['name']]=[dict['password'],int(dict['space'])] #存储格式为{姓名:[密码,可用空间大少]}
            print (n_dict)
            pickle.dump(n_dict,open(os.path.join(A, 'db', 'register'), 'wb'))
            return [dict['name'], int(dict['space'])]
    def help(self, a):
        return False
    def upload(self, list):
        name = list[0]
        b_path = os.path.join(A, 'db', name) #自己的根目录
        h_size = int(list[1]) #自己可用的空间大小
        data = self.request.recv(1024)
        dic = pickle.loads(data)
        f_size = int(dic['size']) #上传文件大小
        filename = dic['filename']
        path = dic['path']
        s_ls = []
        if h_size < f_size:
            a = '0' #空间不足
            s_ls.append(a)
        else:
            a = '1'
            s_ls.append(a)
        if path=='h': #存在根目录
            l_path =os.path.join(b_path,filename)
        else:
            res = path.split('/')
            print (res)
            for i in res:
                b_path = os.path.join(b_path, i) #合拼成子目录
            l_path = os.path.join(b_path,filename) #文件路径

        if os.path.exists(l_path):
            b = '0' #文件已存在
            file_size = os.path.getsize(l_path)
            s_ls.append(b)
            s_ls.append(file_size)
        else:
            b = '1'
            s_ls.append(b)
        if os.path.exists(b_path):
            c = '0'
        else:
            c='1'#文件夹不存在,可以结束
            s_ls.append(c)
            self.request.sendall(pickle.dumps(s_ls))
            return False
        s_ls.append(c)
        self.request.sendall(pickle.dumps(s_ls))
        f_ls = pickle.loads(self.request.recv(1024))#文件以什么方式打开
        self.request.sendall('准备开始'.encode())
        if f_ls[0] =='1':
            f = open(l_path,'wb')
            file_size = 0
        elif f_ls[0]=='2':
            return False
        elif f_ls[0]=='3':#文件名另起
            filename = f_ls[1]
            l_path = os.path.join(b_path,filename)
            f = open(l_path,'wb')
            file_size = 0
        elif f_ls[0]=='4':
            f = open(l_path,'ab')
        else:
            f = open(l_path,'xb')
            file_size = 0
        if f_size == 0:
            f.close()
            return False
        else:
            while file_size< f_size:
                line = self.request.recv(1024)
                f.write(line)
                f.flush()
                file_size += len(line)
            else:
                f.close()
                l_dict = pickle.load(open(os.path.join(A, 'db', 'register'), 'rb'))
                l_dict[name][1] = h_size - f_size #修改已用空间
                pickle.dump(l_dict, open(os.path.join(A, 'db', 'register'), 'wb'))
                return False

    def download(self, list):
        self.request.sendall('ok'.encode())
        path = self.request.recv(1024).decode() #检查文件存在子目录或根目录
        self.request.sendall('check'.encode())
        filename = self.request.recv(1024).decode()
        name = list[0]
        if path == '1':
            l_path = os.path.join(A, 'db', name,filename)
        else:
            data = path.split('/')
            pathname = os.path.join(A, 'db', name)
            for i in data:
                pathname = os.path.join(pathname, i) #合拼子目录
            l_path = os.path.join(pathname,filename)
            print (l_path)
        if os.path.exists(l_path):
            f = open(l_path, 'rb')
            size = os.path.getsize(l_path) #检查文件
            self.request.sendall(str(size).encode()) #要以字符串格式传数字
            data = self.request.recv(1024)
            dic = pickle.loads(data)
            if dic['choose']=='2':
                return False
            elif dic['choose']=='4':
                f.seek(int(dic['size'])) #把指针定位到已下载的地方
            for line in f:
                self.request.sendall(line)
            f.close()
            print ('done')
        else:
            self.request.sendall('该文件不存在'.encode())

    def ls(self, list):
        name = list[0]
        ls = os.listdir(os.path.join(A, 'db', name))
        if len(ls)==0:
            self.request.sendall('0'.encode())
        else:
            a = []
            for i in ls:
                a.append(i) #把存在的文件放入list
            self.request.sendall(pickle.dumps(a))
    def cd(self, list):
        data = self.request.recv(1024).decode()
        name = list[0]
        path = os.path.join(A, 'db', name) #根目录
        path_ls = data.split('/')
        for i in path_ls:
            path = os.path.join(path, i) #合拼子目录
        print (path)
        if os.path.exists(path) is False:
            print (1)
            path = '0'
            self.request.sendall(path.encode())
            return False
        ls = os.listdir(path)
        self.request.sendall(path.encode())
        data = self.request.recv(1024)
        if len(ls)==0:
            self.request.sendall('0'.encode())
        else:
            a = []
            for i in ls:
                a.append(i)
            self.request.sendall(pickle.dumps(a))




    def mkdir(self, a):
        filename = self.request.recv(1024).decode()
        name = a[0]
        if os.path.exists(os.path.join(A, 'db', name,filename)): #检查路径是否存在
            self.request.sendall('文件夹已存在'.encode())
        else:
            os.makedirs(os.path.join(A, 'db', name, filename))
            self.request.sendall('已建好'.encode())






host, port = 'localhost',  9999
server = socketserver.ThreadingTCPServer((host, port), MyTCPHandler)
server.serve_forever()
server

 

posted @ 2017-09-27 19:53  盛欣  阅读(420)  评论(0编辑  收藏  举报