zoukankan      html  css  js  c++  java
  • 高级FTP服务器开发

    要求:

    1. 用户加密认证

    2. 多用户同时登陆

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

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

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

    6. 查看当前目录下文件

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

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

    9. 支持断点续传

    路径如下

    代码

    import socket
    import pickle
    import hashlib
    import sys
    import time
    import os
    A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    class Ftp_client(object):
        def __init__(self):
            self.client = socket.socket()
        def help(self):
            '''
            帮助说明
            :return:
            '''
            print('''请输入正确的指令:
            ls: 查看根目录下文件
            cd: 切换目录
            download: 下载文件
            upload:上文件
            mkdir:新建立文件夹
                    ''')
        def connet(self, ip, port):
            '''
            链接服务器
            :param ip:
            :param port:
            :return:
            '''
            self.client.connect((ip, port))
            data = self.client.recv(1024)
            print(data.decode())
            self.main()
            self.ftp_main()
        def login(self):
            '''
            登录
            :return:
            '''
            name = input('请输入姓名').lower()
            password = input('请输入密码')
            dict = {'name': name, 'password': password}
            self.client.sendall(pickle.dumps(dict))
            data = self.client.recv(1024)
            print(data.decode())
            if data.decode()=='输入有误':
                return False
        def register(self):
            '''
            注册
            :return:
            '''
            while True:
                a = input('请输入注册哪种用户: 1: 普通用户(可用空间10M), 2: VIP用户(可用30M)')
                if a =='1':
                    space = 10485760
                    break
                elif a== '2':
                    space = 31457280
                    break
                else:
                    print('输入有误')
                    continue
            name = input('请输入姓名').lower()
            pd = input('请输入密码')
            dict = {'name': name, 'password': pd, 'space': space}
            self.client.sendall(pickle.dumps(dict))
            data = self.client.recv(1024)
            print(data.decode())
            if data.decode()== '用户名已存在,请重新输入':
                return False
    
        def main(self):
            while True:
                a = input('请输入 1. 用户登录 2. 用户注册 3.退出')
                if a == '1':
                    self.client.sendall('login'.encode())
                    res = self.login()
                elif a == '2':
                    self.client.sendall('register'.encode())
                    res = self.register()
                elif a == '3':
                    exit()
                else:
                    print('输入有误')
                    continue
                if res is False:
                        continue
                else:
                    break
    
        def download(self):
            '''
            下载
            :return:
            '''
            while True:
                data = self.client.recv(1024)
                choose = input('文件所在路径 1 根目录 2 子目录')
                if choose == '1':
                    path = '1'
                    break
                elif choose =='2':
                    path = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入
                    break
                else:
                    print('输入有误')
                    continue
            self.client.sendall(path.encode())
            data = self.client.recv(1024)
            filename = input('请输入下载文件名')
            self.client.sendall(filename.encode())
            size = self.client.recv(1024).decode()
            if size == '该文件不存在':
                print ('该文件不存在')
                return False
            else:
                size = int(size)
                if os.path.exists(os.path.join(A, 'db', filename)):
                    r_size = int(os.path.getsize(os.path.join(A, 'db', filename)))#存在文件的size
                    while True:
                        choose = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载')
                        if choose == '2':
                            dic={}
                            dic['choose'] = choose
                            self.client.sendall(pickle.dumps(dic))
                            return False
                        elif choose == '1':
                            f = open(os.path.join(A, 'db',filename),'wb')
                            r_size = 0
                            break
                        elif choose == '3':
                            name = input('请输入新文件名')
                            f = open(os.path.join(A, 'db',name),'wb')
                            r_size = 0
                            break
                        elif choose == '4':
                            f = open(os.path.join(A, 'db',filename),'ab')
                            break
                        else:
                            print('输入有误,请重新输入')
                    dic={}
                    dic['choose'] = choose
                    dic['size'] = r_size
                    self.client.sendall(pickle.dumps(dic))
                else:
                    r_size = 0
                    f = open(os.path.join(A, 'db', filename),'xb')
    
    
                if size == 0:
                    f.close()
                    print('接收完成')
                else:
                    while  r_size < size:
                        file = self.client.recv(1024)
                        f.write(file)
                        f.flush() #文件强行写入file,预防中途中断
                        r_size += len(file)
                        view_bar(r_size, size)
                        time.sleep(0.1)
                    else:
                        print('接收完成')
                        f.close()
    
    
    
        def upload(self):
            filename = input('请输入上传的文件名')
            if os.path.exists(os.path.join(A, 'db', filename)):
                size = os.path.getsize(os.path.join(A, 'db', filename)) #文件size
                path = input('请输入上传的路径,子路径用 / 分隔隔开, h为根目录')
                dic = {}
                dic['filename'] = filename
                dic['size'] = size
                dic['path'] = path
                self.client.sendall(pickle.dumps(dic))
                f =  open(os.path.join(A, 'db', filename), 'rb')
            else:
                print ('此文件不存在')
                return False
            data = self.client.recv(1024)
            ls = pickle.loads(data) #ls[2]: ;
            if ls[-1]=='1': #ls[-1]:检查下载的路径是否存在
                print ('此路径不存在')
                return False
            if ls[0] == '0':#ls[0]:检查空间够不够;
                print ('空间不足,不能上传')
    
            else:
                if ls[1] == '0': #ls[1]:检查下载是否存在
                    while True:
                        a = input('文件已存在, 1 重新下载 2 停止下载 3 新起名再下载 4 继续下载')
                        f_ls = []
                        f_ls.append(a)
                        if a == '1':
                            break
                        elif a == '2':
                            return False
                        elif a =='3':
                            f_name = input('请输入新文件名')
                            f_ls.append(f_name)
                            break
                        elif a=='4':
                            l_size = ls[2] #ls[2]:已下载的文件大小
                            f.seek(l_size) #把指针放到已下载的地方,继续下载
                            break
                        else:
                            print ('输入有误')
                else:
                    f_ls = []
                    f_ls.append('5') # 5:下载文件不存在
                self.client.sendall(pickle.dumps(f_ls))
                data = self.client.recv(1024).decode()
                print (data)
                for line in f:
                    self.client.sendall(line)
                    num = f.tell() #查看文件上传位置
                    view_bar(num, size)
                    time.sleep(0.1)
                f.close()
                print ('接收完成')
                return False
    
    
        def ls(self):
            data = self.client.recv(1024)
            if data =='0'.encode():
                print('此目录为空')
            elif data =='1'.encode():
                print('此文件不存在')
            else:
                ls = pickle.loads(data)
                print('此文件里有:')
                for i in ls:
                    print(i)
    
        def mkdir(self):
            name = input('请输入文件夹名')
            self.client.sendall(name.encode())
            data = self.client.recv(1024).decode()
            print(data)
    
        def cd(self):
            name = input('请输入路径,子路径用 / 分隔隔开') # 根目录不用输入
            self.client.sendall(name.encode())
            path = self.client.recv(1024).decode()
    
            if path == '0':
                print ('此目录不存在')
                return False
            else:
                print ('所在文件夹路径为 %s' % path)
            self.client.sendall('ok'.encode())
            data = self.client.recv(1024)
            if data =='0'.encode():
                print('此目录为空')
            else:
                ls = pickle.loads(data)
                print('此文件里有:')
                for i in ls:
                    print(i)
    
    
    
        def ftp_main(self):
            while True:
                a = input('请输入相应的指令, help:查询, exit:退出')
                if hasattr(self, a):
                    self.client.sendall(a.encode())
                    func = getattr(self, a)
                    func()
                elif a == 'exit':
                    exit()
                else:
                    self.help()
                    continue
    
    
    
    
    
    
    def view_bar(num, total):
        '''进度条'''
        rate = float(num) / float(total)
        rate_num = int(rate * 100)
        r = '
    %d%%' % (rate_num, ) #
     回到到开头
        sys.stdout.write(r)
        sys.stdout.flush()  #删除记录
    
    ftp = Ftp_client()
    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
  • 相关阅读:
    Statistics Report for pid 21058 on 1wt.eu
    Wget下载终极用法和15个详细的例子
    Importing fabric into your own scripts
    Introduction to Build Profiles
    SSH Programming with Paramiko | Completely Different
    maven repository research webpage
    geek cn tech
    Nginx + Tomcat + Session学习 ﹎敏ō 博客园
    MonoRail 简介
    Linux 中出现的bash: syntax error near unexpected token `
  • 原文地址:https://www.cnblogs.com/nikitapp/p/6541369.html
Copyright © 2011-2022 走看看