基于socketserver开发多线程ftp

Posted zhanlin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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

验证:

用户登录

技术分享图片

 上传

 技术分享图片

技术分享图片

 

 

 

以上是关于基于socketserver开发多线程ftp的主要内容,如果未能解决你的问题,请参考以下文章

106 网络编程实战之基于socketserver实现多用户FTP服务器

基于upd的socketserver,即udp的多线程

基于tcp的socketserver,即tcp的多线程

python学习_day41_socketserver模块

socketserver实现多用户同时在线ftp

python基础----socketserver多并发实现FTP上传多并发