zoukankan      html  css  js  c++  java
  • day-1 用python编写一个简易的FTP服务器

      从某宝上购买了一份《Python神经网络深度学习》课程,按照视频教程,用python语言,写了一个简易的FTP服务端和客户端程序,以前也用C++写过聊天程序,编程思路差不多,但是python编程时更顺畅,代码量更少。没有很高深的理论知识,也不需要扎实的编程基础,知道需要用哪些库就行了。

      两种语言对比,初次感受到python语言的易用之处,python的核心是简洁清晰,也是伪代码的最佳实践语言。可以理解为C语言的一些常用功能库的转义,所以普遍认为python语言带来编程便捷的同时,也会带来一部分性能的牺牲。但这并不也是绝对的,有时候程序的开发效率更为重要,更何况并不一定人人都是大牛,用C或者其它语言未必就能写出比python更高性能的代码。整体学习完以后,还会再来深入对比下python和其他语言的差异。

    1、基本功能

      登陆验证

      上传:支持断点上传,MD5验证

      简单命令:cd、ls、mkdir

    2、常用模块

      下面是我汇总的用到的模块信息,要写出完整的代码,首先得了解基本的python函数和库,例如:

      os,sys库对目录和文件的处理;

      socket和socketserver对网络通信的处理;

      json对传输数据格式的处理;

      configparse对配置文件的处理;

      hashlib对MD5校验的处理;

      optparse对命令行输入参数的格式化处理等;

      另外还需要了解如何利用反射机制,处理多If else分支的函数调用等。

      关于python反射机制,参考链接:https://www.cnblogs.com/Guido-admirers/p/6206212.html

    3、具体代码

      

      ftp_client.py:FTP客户端主程序

    import socket
    import optparse
    import json
    import os,sys
    import hashlib
    
    STATUS_CODE  = {
        250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}",
        251 : "Invalid cmd ",
        252 : "Invalid auth data",
        253 : "Wrong username or password",
        254 : "Passed authentication",
        255 : "Filename doesn'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"
    }
    
    sk = socket.socket()
    sk.connect(("127.0.0.1",18000))
    
    class ClientHander():
        def __init__(self):
    
            #初始化变量
            self.op = optparse.OptionParser()
            self.op.add_option("-s","--server",dest="server")
            self.op.add_option("-P", "--port", dest="port")
            self.op.add_option("-u", "--username", dest="username")
            self.op.add_option("-p", "--password", dest="password")
            self.options,self.args = self.op.parse_args()
            #self.verify_args(self.options,self.args)
            self.mainPath = os.path.dirname(os.path.abspath(__file__))
            self.last = 0
            self.is_authenticated = False
            self.is_connected = False
            self.is_needed_md5 = True
    
            #开始建立连接
            self.make_connection()
    
        # 验证用户输入信息
        def verify_args(self,options,args):
            server = options.server
            port = options.port
            username = options.username
            password = options.password
            if int(port) > 0 and int(port) < 65535:
                return True
            else:
                exit("the port should been in 0-65535")
    
        # 连接服务器
        def make_connection(self):
            if not self.is_connected:
                self.sock = socket.socket()
                self.sock.connect(("127.0.0.1", 18000))
                self.is_connected = True
            return
    
        def interactive(self):
    
            # 判断是否已经验证过
            if not self.is_authenticated:
                self.make_connection()
                self.authenticate()
    
            # 开始和服务器进行交互
            cmd_info = input("[%s]"%self.current_dir).strip()
            # 利用反射机制,建立命令与功能对应关系
            cmd_list = cmd_info.split()
            if hasattr(self,cmd_list[0]):
                func = getattr(self,cmd_list[0])
                func(*cmd_list)
    
        def authenticate(self):
            if not self.is_connected:
                print("没有连接FTP服务器")
                return
            if self.options.username is None or self.options.password is None:
                username = input("username:")
                password = input("password:")
                return self.get_auth_result(username,password)
            return self.get_auth_result(self.options.username,self.options.password)
    
        def response(self):
            data = self.sock.recv(1024).decode("utf-8")
            data = json.loads(data)
            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"))
            res = self.response()
            print("res",res["status_code"])
            if res["status_code"] ==  254:
                self.username = username
                self.current_dir = username
                self.is_authenticated = True
                print(STATUS_CODE[254])
                return True
            else:
                print(STATUS_CODE[res["status_code"]])
    
        def put(self,*cmd_list):
    
            # 1 发送上传命令
            action,local_path,target_path = cmd_list
            local_path = os.path.join(self.mainPath,local_path)
            file_name = os.path.basename(local_path)
            file_size = os.stat(local_path).st_size
            data = {
                "action":"put",
                "file_name": file_name,
                "file_size": file_size,
                "target_path": target_path
            }
            self.sock.sendall(json.dumps(data).encode("utf-8"))
    
            # 2 接收服务器检查结果
            has_sent = 0
            is_exsit = self.sock.recv(1024).decode("utf-8")
    
            # 3 根据服务器检查结果,执行相应命令
            # 文件存在但不完整
            if is_exsit == "800":
                print("服务器文件已经存在,且不完整")
                # 和服务器确认是否继续上传
                choice = input("The file exist but not enough,is continue?[Y/N]").strip()
                # 继续上传
                if choice.upper() == "Y":
                    self.sock.sendall("Y".encode("utf-8"))
                    # 等待服务器返回存在文件大小
                    continue_position = self.sock.recv(1024).decode("utf-8")
                    has_sent += int(continue_position)
                # 不继续上传
                else:
                    self.sock.sendall("N".encode("utf-8"))
            # 文件存在且完整
            elif is_exsit == "801":
                print("服务器文件已经存在,且完整")
                return
            f = open(local_path,"rb")
            f.seek(has_sent)
    
            md5_obj = None
            if self.is_needed_md5:
                md5_obj = hashlib.md5()
    
            while has_sent < file_size:
                data = f.read(1024)
                self.sock.sendall(data)
                if md5_obj:
                    md5_obj.update(data)
                has_sent += len(data)
    
            mdt_val_server = self.sock.recv(1024).decode("utf-8")
            mdt_val_client = md5_obj.hexdigest()
            self.show_progress(has_sent,file_size)
            print("client's md5 is %s"%mdt_val_client)
            print("server's md5 is %s" %mdt_val_server)
            f.close()
    
            if mdt_val_client == mdt_val_server:
                print("put success")
            else:
                print("Athenticate MD5 failed.")
    
        def show_progress(self,has,total):
            rate = float(has) / float(total)
            rate_num = int(rate*100)
            print("%s%% %s
    "%(rate_num,"#"*rate_num))
    
        def ls(self,*cmd_list):
            data = {
                "action":"ls"
            }
            self.sock.sendall(json.dumps(data).encode("utf-8"))
            data = self.sock.recv(1024).decode("utf-8")
            print(data)
    
        def cd(self,*cmd_list):
            # cd images
            data = {
                "action":"cd",
                "dirname":cmd_list[1]
            }
            self.sock.sendall(json.dumps(data).encode("utf-8"))
            data = self.sock.recv(1024).decode("utf-8")
            self.current_dir = os.path.basename(data)
            print(os.path.basename(data))
    
        def mkdir(self,*cmd_list):
            data = {
                "action":"mkdir",
                "dirname":cmd_list[1]
            }
    
            self.sock.sendall(json.dumps(data).encode("utf-8"))
            data = self.sock.recv(1024).decode("utf-8")
            print(data)
    
    ch = ClientHander()
    while 1:
        ch.interactive()
    View Code

      ftp_server.py:FTP服务器端主程序

    import os,sys
    
    PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(PATH)
    
    from core import main
    
    if __name__ == '__main__':
        main.ArgvHandler()
    View Code

      main.py:处理参数输入

    import optparse
    import socketserver
    from conf import settings
    from core import server
    
    class ArgvHandler():
    
        def __init__(self):
            self.op = optparse.OptionParser()
            #self.op.add_option("-s","--server",dest = "server")
            #self.op.addd_option("-P", "--port", dest="port")
    
            options,args = self.op.parse_args()
            self.verify_args(options,args)
    
    
        def verify_args(self,options,args):
            #首先判断参数的长度
            if len(args) < 1:
                print("参数个数太少,请重新输入.")
            else:
                cmd = args[0]
                if hasattr(self,cmd):
                    func = getattr(self,cmd)
                    func()
            self.start()
    
        def start(self):
            s = socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.ServerHandler)
            print("服务器已经启动")
            s.serve_forever()
    
        def help(self):
            pass
    View Code

      server.py:具体代码实现

    import socketserver
    import json
    import configparser
    from conf import settings
    import os
    import hashlib
    
    STATUS_CODE  = {
    
        250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}",
        251 : "Invalid cmd ",
        252 : "Invalid auth data",
        253 : "Wrong username or password",
        254 : "Passed authentication",
        255 : "Filename doesn'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 ServerHandler(socketserver.BaseRequestHandler):
        def handle(self):
            while 1:
                data = self.request.recv(1024).strip()
                if not data:continue
                data = json.loads(data.decode("utf-8"))
    
                if data.get("action"):
                    if hasattr(self,data.get("action")):
                        func = getattr(self,data.get("action"))
                        func(**data)
                    else:
                        print("Invalid cmd")
                else:
                    print("Invalid cmd")
    
        def send_response(self,status_code):
            response = {"status_code":status_code}
            self.request.sendall(json.dumps(response).encode("utf8"))
    
        def auth(self,**data):
            username = data["username"]
            password = data["password"]
            print("收到客户端验证请求,用户名:",username,"密码:",password)
            user = self.authenticate(username,password)
            if user:
                self.send_response(254)
            else:
                self.send_response(253)
    
        def authenticate(self,username,password):
            cfg = configparser.ConfigParser()
            cfg.read(settings.ACCOUNT_PATH)
            print("正在读取配置用户配置信息:",settings.ACCOUNT_PATH)
    
            if username in cfg.sections():
                if cfg[username]["Password"] == password:
                    self.useranme = username
                    self.mainPath = os.path.join(settings.BASE_DIR,"home",self.useranme)
                    print("成功匹配上用户信息,用户密码:", cfg[username]["Password"])
                    print("用户根目录为:",self.mainPath)
                    return username
    
        def put(self,**data):
            print("data",data)
            file_name = data.get("file_name")
            file_size = data.get("file_size")
            target_path = data.get("target_path")
    
            abs_path = os.path.join(self.mainPath,target_path,file_name)
    
            has_received = 0
    
            if os.path.exists(abs_path):
                file_has_size = os.stat(abs_path).st_size
                if file_has_size < file_size:
                    #断点续传
                    self.request.sendall("800".encode("utf-8"))
                    choice = self.request.recv(1024).decode("utf-8")
                    if choice.upper() == "Y":
                        self.request.sendall(str(file_has_size).encode("utf-8"))
                        f = open(abs_path,"ab")
                        has_received += file_has_size
                    else:
                        f = open(abs_path,"wb")
                else:
                    #文件完全存在
                    self.request.sendall("801".encode("utf-8"))
                    return
            else:
                self.request.sendall("802".encode("utf-8"))
                f = open(abs_path, "wb")
    
            md5_obj = hashlib.md5()
    
            while has_received < file_size:
                try:
                    data = self.request.recv(1024)
                    f.write(data)
                    md5_obj.update(data)
                    has_received += len(data)
                except Exception as e:
                    print("传输文件发生异常:",str(e))
                    break
    
            # 给客户端发送MD5
            md5_val = md5_obj.hexdigest()
            self.request.sendall(md5_val.encode("utf-8"))
    
            f.close()
    
        def ls(self,**data):
            file_list = os.listdir(self.mainPath)
            print("ls %s"%self.mainPath)
            if not file_list:
                file_str = "<empty dir>"
            else:
                file_str = "
    ".join(file_list)
            self.request.sendall(file_str.encode("utf-8"))
    
        def cd(self,**data):
            dir_name = data.get("dirname")
            print("cd %s
    "%dir_name)
            if dir_name == "..":
                self.mainPath = os.path.dirname(self.mainPath)
            else:
                self.mainPath = os.path.join(self.mainPath,dir_name)
    
            self.request.sendall(self.mainPath.encode("utf-8"))
    
        def mkdir(self,**data):
            dirname = data.get("dirname")
            path = os.path.join(self.mainPath,dirname)
            if not os.path.exists(path):
                if "/" in dirname:
                    os.makedirs(path)
                else:
                    os.mkdir(path)
                self.request.sendall("create success".encode("utf-8"))
            else:
                self.request.sendall("dirname exist".encode("utf-8"))
    View Code

      accounts.cfg:账户信息,登陆验证用

    [DEFAULT]
    [tanbiao]
    Password = tanbiao
    Quotation = 100
    
    [root]
    Password = root
    Quotation = 100
    View Code

      setting.py:配置信息

    import os
    
    BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    IP="127.0.0.1"
    PORT=18000
    
    
    ACCOUNT_PATH=os.path.join(BASE_DIR,"conf","accounts.cfg")
    View Code
  • 相关阅读:
    修改tomcat的get方法的参数长度
    [转]修改hosts文件不起作用
    [转]solr DataImportHandler 解决mysql 表导入内存溢出问题
    plat模板修改记录
    Linux基本命令
    使用DTM ( Dynamic Topic Models )进行主题演化实验
    Stuts2的"struts.devMode"设置成true后,不起作用,仍需要重启tomcat
    R语言——绘图函数深入学习
    R语言——基本绘图函数
    Stem Cell 华人科学家
  • 原文地址:https://www.cnblogs.com/python-frog/p/8636004.html
Copyright © 2011-2022 走看看