1. 程式人生 > >基於socketserver開發多線程ftp

基於socketserver開發多線程ftp

邏輯 += 登錄 provide pen edi tell %s tar

完成功能:

  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