zoukankan      html  css  js  c++  java
  • 基于socketserver开发多线程ftp

    完成功能:

    1. 用户加密认证
    2. 允许同时多用户登录
    3. 每个用户有自己的家目录 ,且只能访问自己的家目录
    4. 对用户进行磁盘配额,每个用户的可用空间不同
    5. 允许用户在ftp server上随意切换目录
    6. 允许用户查看当前目录下文件
    7. 允许上传和下载文件
    8. 文件传输过程中显示进度条
    9. 附加功能:支持文件的断点续传

    目录结构:

    ftpclient:

    ftp_client.py

    ftpserver:

    bin 

    ftp_server.py 启动文件

    conf 

    settings 配置文件

    account.cfg 用户信息

    core 存放逻辑代码

    main 主程序入口

    server 逻辑处理

    home用户家目录

    ftp客户端代码

    # _*_ coding:utf-8 _*_
    # Auother Jerry
    import socket
    import optparse, os, sys
    import json
    
    # 服务端返回码对应内容
    STATUS_CODE = {
        250: 'Invalid cmd format,e.g:{"action":"get","filename":"test.py","size":443}',
        251: "Invalid cmd",
        252: 'Invalid auth data',
        253: 'Wrong username or password',
        254: 'Passwed authentication',
        255: "Filename doest't provided",
        256: "File doesn't exist on server",
        257: "ready to send file",
        258: 'md5 verification',
        800: 'the file exist,but not enough,is continue?',
        801: 'the file exist!',
        802: 'ready to receive datas',
        900: 'md5 valdate success'
    }
    
    
    class ClientHandler(object):
        def __init__(self):
            self.parser = optparse.OptionParser()
            self.sock = None
            self.user = None
            self.mainPath = os.path.dirname(os.path.abspath(__file__))
            self.parser.add_option("-s", "--server", dest="server")
            self.parser.add_option("-P", "--port", dest="port")
            self.parser.add_option("-u", "--username", dest="username")
            self.parser.add_option("-p", "--password", dest="password")
            self.options, self.args = self.parser.parse_args()
            self.verify_args()
            self.make_connect()
    
        # 确认端口在0~65535之间
        def verify_args(self):
            port = self.options.port
            if int(port) > 0 and int(port) < 65535:
                return True
            else:
                exit('port must in 0~65535')
    
        # 创建连文件
        def make_connect(self):
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((self.options.server, int(self.options.port)))
    
        def authenticate(self):
            if self.options.username is None and self.options.password is None:
                username = input('username: ').strip()
                password = input('password: ').strip()
            elif self.options.username:
                username = self.options.username
                if self.options.password is None:
                    password = input('password: ').strip()
                else:
                    password = self.options.password
            else:
                print(self.parser.print_help())
                return
            return self.get_auth_result(username, password)
    
        def interactive(self):
            if self.authenticate():
                while True:
                    cmd_info = input('[%s]' % self.user).strip()
                    if cmd_info == 'q':
                        exit()
                    elif len(cmd_info) == 0:continue
                    cmd_list = cmd_info.split()
                    if hasattr(self, cmd_list[0]):
                        func = getattr(self, cmd_list[0])
                        func(*cmd_list)
    
        def response(self):
            ret = self.sock.recv(1024).decode('utf-8')
            data = json.loads(ret)
            return data
    
        def get_auth_result(self, username, password):
            data = {
                "action": "auth",
                "username": username,
                "password": password
            }
            self.sock.send(json.dumps(data).encode('utf-8'))
            ret = self.sock.recv(1024).decode('utf-8')
            data = json.loads(ret)
            status_code = data['status_code']
            if status_code == 254:
                print(STATUS_CODE[254])
                self.user = username
                return True
            else:
                print(STATUS_CODE[status_code])
    
        def put(self, *args):
            action, local_file, target_path = args
            local_file = os.path.join(self.mainPath, local_file)
            if local_file:
                file_name = os.path.basename(local_file)
                file_size = os.stat(local_file).st_size
                data = {
                    "action": "put",
                    "file_name": file_name,
                    "file_size": file_size,
                    "target_path": target_path
                }
                print('send data info ', data)
                self.sock.send(json.dumps(data).encode('utf-8'))
                code = int(self.sock.recv(1024).decode("utf-8"))
                print('receive code ', code)
                send_size = 0
                with open(local_file, 'rb') as f:
                    if code == 800:
                        print(STATUS_CODE[code])
                        choice = input('the file exist,but not enough,is continue?Y|N').strip()
                        self.sock.sendall(choice.encode('utf-8'))
                        if choice == 'Y' or choice == 'y':
                            current_seek = self.sock.recv(1024).decode('utf-8')
                            send_size = int(current_seek)
                            f.seek(int(current_seek))
                    elif code == 801:
                        print(STATUS_CODE[code])
                        return
                    else:
                        print(STATUS_CODE[code])
    
                    while send_size < file_size:
                        send_data = f.read(1024)
                        self.sock.send(send_data)
                        send_size += len(send_data)
                        self.show_process(send_size,file_size)
    
        def show_process(self, send_size, file_size):
            per = int(float(send_size) / float(file_size) * 100)
            if send_size == file_size:
                sys.stdout.write('succeed! %s
    ' % '#' * per)
            else:
                sys.stdout.write('%s%%%s
    ' % (per, '#' * per))
    
        def ls(self,*args):
            data = {
                'action': 'ls',
            }
            self.sock.send(json.dumps(data).encode('utf-8'))
            res = self.sock.recv(1024).decode('utf-8')
            print(res)
    
        def cd(self,*args):
            data = {
                "action": "cd",
                'cd_path': args[1]
            }
            self.sock.send(json.dumps(data).encode('utf-8'))
            res = self.sock.recv(1024).decode('utf-8')
            print('[%s]' % res)
    
    
    client = ClientHandler()
    client.interactive()
    View Code

    ftp服务端代码:

    # _*_ coding:utf-8 _*_
    # Auother Jerry
    import os,sys
    
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASE_DIR)
    from core import main
    
    
    if __name__ == '__main__':
        main.ArgvHandler()
    ftp_server.py
    [DEFAULT]
    
    [jerry]
    username=jerry
    password=123
    
    [root]
    username=root
    password=123
    accounts.cfg
    # _*_ coding:utf-8 _*_
    # Auother Jerry
    import os
    
    
    BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    IP='127.0.0.1'
    PORT=8080
    
    ACCOUNT_PATH = os.path.join(BASE_DIR,'conf','accounts.cfg')
    
    STATUS_CODE = {
        250:'Invalid cmd format,e.g:{"action":"get","filename":"test.py","size":443}',
        251:"Invalid cmd",
        252:'Invalid auth data',
        253:'Wrong username or password',
        254:'Passwed authentication',
        255:"Filename doest't provided",
        256:"File doesn't exist on server",
        257:"ready to send file",
        258:'md5 verification',
        800:'the file exist,but not enough,is continue?',
        801:'the file exist!',
        802:'ready to receive datas',
        900: 'md5 valdate success'
    }
    
    HOME_DIR = os.path.join(BASE_DIR,'home')
    settings
    # _*_ coding:utf-8 _*_
    # Auother Jerry
    import optparse
    import socketserver
    from conf import settings
    from core import server
    
    
    class ArgvHandler(object):
        def __init__(self):
            self.parser = optparse.OptionParser()
    
            options, args = self.parser.parse_args()
    
            self.verify_args(options, args)
    
        def verify_args(self, options, args):
            cmd = args[0]
            print('first argv: ', cmd)
            if cmd:
                if hasattr(self, cmd):
                    func = getattr(self, cmd)
                    func()
    
        def start(self):
            print('start socketserver')
            sk = socketserver.ThreadingTCPServer((settings.IP, settings.PORT), server.ServerHandler)
            sk.serve_forever()
    main
    # _*_ coding:utf-8 _*_
    # Auother Jerry
    import socketserver
    import json, os
    import configparser
    from conf import settings
    
    
    class ServerHandler(socketserver.BaseRequestHandler):
        def handle(self):
            try:
                while True:
                    data = self.request.recv(1024).decode('utf-8')
                    data = json.loads(data)
                    print(data)
                    if data.get('action', None):
                        if hasattr(self, data.get('action')):
                            func = getattr(self, data.get('action'))
                            func(**data)
            except Exception as e:
                print(e)
    
        def auth(self, **kwargs):
            username = kwargs['username']
            password = kwargs['password']
            if self.authenticate(username, password):
                self.send_response(254)
            else:
                self.send_response(253)
    
        def authenticate(self, username, password):
            cfg = configparser.ConfigParser()
            cfg.read(settings.ACCOUNT_PATH)
            if username in cfg.sections():
                if cfg[username]['password'] == password:
                    self.user = username
                    self.home = os.path.join(settings.HOME_DIR, self.user)
                    self.mainPath = self.home
                    if not os.path.exists(self.home):
                        os.makedirs(self.home)
                    return username
    
        def send_response(self, state_code):
            response = {'status_code': state_code}
            self.request.send(json.dumps(response).encode('utf-8'))
    
        def put(self, **kwargs):
            file_name = kwargs['file_name']
            target_path = kwargs['target_path']
            file_size = kwargs['file_size']
            abs_path = os.path.join(self.home, target_path, file_name)
            print('ready receive file ', abs_path)
            send_size = 0
            if os.path.exists(abs_path):
                file_has_size = os.stat(abs_path).st_size
                if file_has_size < file_size:
                    f = open(abs_path, 'ab')
                    self.request.sendall("800".encode('utf-8'))
                    choice = self.request.recv(1024).decode('utf-8')
                    if choice == 'Y' or choice == 'y':
                        current_seek = f.tell()
                        self.request.sendall(str(current_seek).encode('utf-8'))
                    else:
                        pass
                else:
                    self.request.sendall('801'.encode('utf-8'))
                    return
            else:
                if not os.path.exists(os.path.dirname(abs_path)):
                    print('make dir ', os.path.dirname(abs_path))
                    os.makedirs(os.path.dirname(abs_path))
                self.request.sendall('802'.encode('utf-8'))
                f = open(abs_path, 'wb')
            print('start store file')
            while send_size < file_size:
                data = self.request.recv(1024)
                f.write(data)
                send_size += len(data)
            f.close()
    
        def ls(self, **kwargs):
            file_list = os.listdir(self.mainPath)
            if not len(file_list):
                file_str = '<empty dir>'
            else:
                file_str = '
    '.join(file_list)
            self.request.send(file_str.encode('utf-8'))
    
        def cd(self, **kwargs):
            cd_path = kwargs['cd_path']
            if cd_path == '..':
                if self.mainPath == self.home:
                    self.mainPath = self.mainPath
                else:
                    self.mainPath = os.path.dirname(self.mainPath)
            elif cd_path == '.':
                pass
            else:
                if os.path.exists(os.path.join(self.mainPath, cd_path)):
                    self.mainPath = os.path.join(self.mainPath, cd_path)
                else:
                    pass
            self.request.send(os.path.basename(self.mainPath).encode('utf-8'))
    server.py

    验证:

    用户登录

     上传

     

     

     

    
    
  • 相关阅读:
    IEnumerable<T>转DataTable的几种方法
    关于IAsyncResult接口的CompletedSynchronously属性
    为WCF增加UDP绑定(储备篇)
    WPF自定义集合控件概述与遇到的问题
    WPF嵌套模板引发的血案
    为WCF增加UDP绑定(实践篇)
    Uva 10557 XYZZY(DFS+BFS)
    Uva 572 Oil Deposits(DFS)
    Uva 532 Dungeon Master(三维迷宫)
    Uva 10004 Bicoloring
  • 原文地址:https://www.cnblogs.com/zhanlin/p/8794794.html
Copyright © 2011-2022 走看看