基于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\r' % '#' * per)
        else:
            sys.stdout.write('%s%%%s\r' % (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 = '\r'.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

验证:

用户登录

 上传

 

 

 

 


posted @ 2018-04-11 11:46  Jerry-lin  阅读(157)  评论(0编辑  收藏  举报