• python作业


     github源码:https://github.com/xieyousheng/ftp

     思路分析:

    作者: xieyousheng
    版本:Ftp_v1
    开发环境: python3.6.4
    
    程序介绍:
    
    1. 用户认证
    2. 多用户同时登陆
    3. 每个用户有自己的家目录且只能访问自己的家目录
    4. 对用户进行磁盘配额、不同用户配额可不同
    5. 用户可以登陆server后,可切换目录
    6. 查看当前目录下文件
    7. 上传下载文件
    8. 传输过程中现实进度条
    9. 支持断点续传
    10.可通过root对用户操作
    
    使用说明:
    1.可以在Linux和Windows都可以运行
    2.root用户可以调用所有命令
    3.其他用户只能调用了cd,ls,mkdir,rm,wget,put,命令
    
    服务端启动命令
        python ftp_server.py start
    客户端启动命令
        python ftp_client -s 服务端地址 -P 服务端端口 -u 用户名 -p 密码 
    
    put 上传
    wget 下载
    mkdir 创建目录
    ls  查看文件信息
    rm  删除文件或目录
    cd 切换目录
    useradd 添加用户
    usermod 修改用户
    userdel 删除用户
    
    服务端与客户端的功能对应,通过自省/反射来映射功能(hasattr、getattr)

    文件目录结构

    #coding=utf8
    import optparse
    from socket import *
    import json
    import os,sys
    import struct
    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",
        259 :   "Insufficient space left",
    
        800 :   "the file exist ,but not enough , is continue?",
        801 :   "the file exist!",
        802 :   "ready to receive datas",
        803 :   "User already exists",
        804 :   "This directory is used by other users",
        805 :   "This user does not exist",
        806:    "Delete home directory or not",
    
        900 :   "md5 valdate success",
        901: "OK",
        902: "the directory exist!"
    }
    
    class ClientHandler():
        def __init__(self):
            '''
            初始化,通过make_connection()得到一个socket连接,
            然后执行handler通信处理函数
            '''
            self.op = optparse.OptionParser()
            self.op.add_option('-s','--server',dest="server",help="server name or server ip")
            self.op.add_option('-P', '--port', dest="port", help="server port(0-65535)")
            self.op.add_option('-u', '--username', dest="username", help="username")
            self.op.add_option('-p', '--password', dest="password", help="password")
            self.options,self.argv = self.op.parse_args()
            self.main_path = os.path.dirname(os.path.abspath(__file__))
            self.verify_argv(self.options,self.argv)
            self.make_connection()
            self.handler()
    
        def handler(self):
            '''
            如果认证通过就进入通信循环,
            接受用户输入的命令,如果是quit或者exit就退出,输入命令为空就跳过此次循环
            根据用户输入的命令通过hasattr与getattr进行分发功能
            如果没有通过,接关闭socket连接
            :return:
            '''
            if self.authenticate():
                while True:
                    cmd_info = input(self.pwd).strip()
                    if cmd_info == 'quit' or cmd_info == 'exit':exit()
                    if not cmd_info: continue
                    cmd_list = cmd_info.split()
                    if hasattr(self,cmd_list[0]):
                        func = getattr(self,cmd_list[0])
                        func(*cmd_list)
                    else:
                        print("无效的命令")
            else:
                self.sock.close()
    
        def authenticate(self):
            '''
            认证判断用户输入的username和password是否为None
            如果是就提示用户输入,然后进入认证get_auth_result
            如果不是为None就直接认证get_auth_result
            :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 get_auth_result(self,username,password):
            '''
            认证函数
            准备一个字典,字典中的action 是固定的 "auth"对应服务端的auth功能,认证功能字典中应该带有用户名和密码
            把字典传给resphonse功能进行发送给服务端
            通过request功能进行接受服务端发过来的字典
            判断服务端发来的状态码
            如果为254 即  254 :   "Passed authentication" 认证通过,
            就把self.user = username
            self.pwd = 服务端发送过来的路径
            如果不为254,直接输入状态码信息
            :param username:
            :param password:
            :return:
            '''
            data = {
                "action": "auth",
                "username":username,
                "password":password
            }
            self.resphonse(data)
            res = self.request()
            if res['status_code'] == 254:
                self.user = username
                print(STATUS_CODE[res['status_code']])
                self.pwd = res["bash"]
                return True
            else:
                print(STATUS_CODE[res['status_code']])
    
        def request(self):
            '''
            接受功能函数,
            从服务端接受包的长度,然后再从服务端接受包,这样可以解决粘包的问题,这里包编码前的格式为json
            接收到包之后进行解码,然后把json字符串转为为原有的格式(字典)
    
            :return:
            '''
            length = struct.unpack('i',self.sock.recv(4))[0]
            data = json.loads(self.sock.recv(length).decode('utf-8'))
            return data
    
        def resphonse(self,data):
            '''
            发送功能
            把接受到的字典,转换为json字符串然后进行编码
            使用struct.pack封装json字符串的长度
            向服务端发送长度,然后再发送已经编码的json字符串
            :param data:
            :return:
            '''
            data = json.dumps(data).encode('utf8')
            length = struct.pack('i',len(data))
            self.sock.send(length)
            self.sock.send(data)
    
        def make_connection(self):
            '''
            创建连接
            :return: 
            '''
            self.sock = socket(AF_INET,SOCK_STREAM)
            self.sock.connect((self.options.server,int(self.options.port)))
    
        def verify_argv(self,options,argv):
            '''
            端口参数验证
            :param options: 
            :param argv: 
            :return: 
            '''
            if int(options.port) > 0 and int(options.port) < 65535:
                return True
            else:
                exit("端口范围0-65535")
    
        def processbar(self,num,total):  # 进度条
            rate = num / total
            rate_num = int(rate * 100)
            is_ok = 0
            if rate_num == 100:
                r = '
    %s>%d%%
    ' % ('=' * rate_num, rate_num,)
                is_ok = 1
            else:
                r = '
    %s>%d%%' % ('=' * rate_num, rate_num,)
            sys.stdout.write(r)
            sys.stdout.flush
            return is_ok
    
    
        def put(self,*cmd_list):
            cmd_list = cmd_list[1:]
            if not cmd_list:
                print("请输入要上传的文件路径!")
                return
            file_path = os.path.join(self.main_path,cmd_list[0])
            filename = os.path.basename(cmd_list[0])
            filesize = os.path.getsize(file_path)
            data = {
                "action" : "put",
                "filename" : filename,
                "filesize" :filesize
            }
            if len(cmd_list) == 1:
                data['target_path']= "."
            else:
                data['target_path'] = cmd_list[1]
    
            self.resphonse(data)
    
            is_exist = self.request()
            f = open(file_path,'rb')
            if is_exist["status_code"] == 802:
                has_received = 0
                f.seek(has_received)
    
            elif is_exist["status_code"] == 801 or is_exist["status_code"] == 259:
                print(STATUS_CODE[is_exist["status_code"]])
                return
            elif is_exist["status_code"] == 800:
                u_choice = input("the file exist,but not enough,is continue?[Y/N]").strip()
                self.resphonse({"choice": u_choice.upper()[0]})
                if u_choice.upper()[0] == "Y":
                    has_received = self.request()['has_received']
                    f.seek(has_received)
                else:
                    has_received = 0
                    f.seek(has_received)
    
            while has_received < filesize:
                file_data = f.read(1024)
                self.sock.send(file_data)
                has_received += len(file_data)
                self.processbar(has_received,filesize)
            f.close()
    
            print("put success!")
    
        def mkdir(self,*cmd_list):
            data = {
                "action" : "mkdir",
                "dirname": cmd_list[1:]
            }
            self.resphonse(data)
            res = self.request()
            if res["status_code"] != 901:
                print(STATUS_CODE[res["status_code"]])
    
        def rm(self,*cmd_list):
            data = {
                "action":"rm",
                "dirname":cmd_list[1:]
            }
            self.resphonse(data)
            res = self.request()
    
    
        def cd(self,*cmd_list):
            if len(cmd_list)==1 : return
            data = {
                "action" : "cd",
                "dirname" : cmd_list[1]
            }
            self.resphonse(data)
            res = self.request()
            self.pwd = res["bash"]
    
        def ls(self,*cmd_list):
            data = {
                "action": "ls",
            }
            if len(cmd_list) == 1:
                data["dirname"] = "."
            else:
                data["dirname"] = cmd_list[1]
            self.resphonse(data)
            res = self.request()
            if res["status_code"] == 903 :
                print(res["data"])
            else:
                print('
    '.join(res["data"]))
    
        def useradd(self,*cmd_list):
            if self.user != 'root':
                print("你无权限执行此命令!")
                return
            data = {
                "action" : "useradd",
            }
            data = self.useradd_verify_argv(*cmd_list,**data)
            self.resphonse(data)
            print(STATUS_CODE[self.request()["status_code"]])
    
    
        def useradd_verify_argv(self,*cmd_list,**data):
            op = optparse.OptionParser()
            op.add_option('-u', '--username', dest="username")
            op.add_option('-p', '--password', dest="password")
            op.add_option('-d', '--drictory', dest="drictory")
            op.add_option('-m', '--maxsize', dest="maxsize")
            options, argv = op.parse_args(list(cmd_list))
            data["username"] = options.username
            data["password"] = options.password
            data["home"] = options.drictory
            data["homemaxsize"] = options.maxsize
            if data["username"] is None: data["username"] = input("username : ")
            if data["action"] == "useradd":
                if data["password"] is None: data["password"] = input("password : ")
            if data["home"] is None: data["home"] = data["username"]
            if data["homemaxsize"] is None: data["homemaxsize"] = 1000
            return data
    
        def usermod(self,*cmd_list):
            if self.user != 'root':
                print("你无权限执行此命令!")
                return
            data = {
                "action" : "usermod",
            }
            data = self.useradd_verify_argv(*cmd_list, **data)
            self.resphonse(data)
            print(STATUS_CODE[self.request()["status_code"]])
    
        def userdel(self,*cmd_list):
            if self.user != 'root':
                print("你无权限执行此命令!")
                return
            data = {
                "action":"userdel",
                "username":cmd_list[1]
            }
            self.resphonse(data)
            res = self.request()
            if res["status_code"] == 805:
                print(STATUS_CODE[res["status_code"]])
                return
            choice = input("Delete home directory or not,Y/N:").strip()
            self.sock.send(choice.upper()[0].encode('utf8'))
            print(STATUS_CODE[res["status_code"]])
    
        def wget(self,*cmd_list):
            data = {
                "action" : "wget",
            }
            file_path = os.path.dirname(os.path.abspath(__file__))
            if len(cmd_list) == 1:
                print("请输入文件名!")
                return
            elif len(cmd_list) >= 3:
                if os.path.isabs(cmd_list[2]):
                    file_path = cmd_list[2]
                    if not os.path.exists(cmd_list[2]):
                        print("目标路径不存在!")
                        return
                else:
                    file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),cmd_list[2])
                    if not os.path.exists(file_path):
                        print("目标路径不存在!")
                        return
    
    
            data["filename"] = cmd_list[1]
            self.resphonse(data)
            res = self.request()
            if res["status_code"] == 256:
                print(STATUS_CODE[res["status_code"]])
                return
            try:
                f = open(os.path.join(file_path,os.path.basename(data["filename"])),'wb')
    
            except PermissionError as e:
                print(e)
                self.sock.send('0'.encode('utf-8'))
                return
            if res["filesize"] == 0 :
                f.close()
                return
            self.sock.send('1'.encode('utf-8'))
            size = 0
            while True:
                file_data = self.sock.recv(4096)
                f.write(file_data)
                size += len(file_data)
                if self.processbar(size,res["filesize"]):
                    break
    
            f.close()
    
    
    
    
    
    
    
    
    if __name__ == '__main__':
        c = ClientHandler()
        c.handler()
    ftp_client.py
    import os,sys
    
    BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    sys.path.append(BASEDIR)
    
    from src import main
    
    if __name__ == '__main__':
        main.ArgvHandler()
    ftp_server.py
    [root]
    username = root
    password = 123
    home = root
    homemaxsize = 1000
    
    [xie]
    username = xie
    password = 123
    home = xie
    homemaxsize = 1000
    account.cfg
    import os
    BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    IP = '0.0.0.0'
    PORT = 8090
    HOME_PATH = os.path.join(BASEDIR,'data')
    ACCOUNT_PATH = os.path.join(BASEDIR,'conf','account.cfg')
    
    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",
    
        259 :   "Insufficient space left",
    
        800 :   "the file exist ,but not enough , is continue?",
        801 :   "the file exist!",
        802 :   "ready to receive datas",
    
        803 :   "User already exists",
        804 :   "This directory is used by other users",
        805 :   "This user does not exist",
    
        900 :   "md5 valdate success",
    
        901 :   "OK",
    
        902 :   "the directory exist!",
    
        903 :   "the directory not exist!",
    
        904 :   "No such file or directory"
    }
    setting.py
    import os
    from conf import setting
    
    '''
    服务端处理客户的类
    
    '''
    
    class Client:
    
        def __init__(self,user,passwd,home,maxsize):
            '''
    
            :param user:    用户名
            :param passwd:  密码
            :param home:    家目录
            :param maxsize: 家目录空间大小
            self.basedir ftp的根目录,所有家目录都是从这个目录开始
            self.path 目录列表,最开始的是家目录,然后添加到列表的是cd的目录,这里就限制了用户的根目录就是自己的家目录
            self.free 表示用户家目录剩余的空间大小
            '''
            self.user = user
            self.passwd = passwd
            self.basedir = setting.HOME_PATH
            self.home = home
            self.path = [self.home]
            self.maxsize = maxsize
            self.free = float(maxsize) - float(self.use_size()/1024)
    
        def get_bash(self):
            return '%s @ %s >>> :' % (self.user,self.path[-1])
    
    
        def use_size(self):
            '''
            用户家目录已使用的大小
            :param path:
            :return:
            '''
            size = 0
            for i in os.listdir(os.path.join(self.basedir,self.home)):
                if os.path.isdir(i):
                    size += self.use_size(os.path.join(self.basedir,self.home,i))
                size += os.path.getsize(os.path.join(self.basedir,self.home,i))
            size = float(size/1024)
            #由于系统4K 对齐,所以最小存储单位为 4KB
            res = divmod(size,4)
            if res[1]:
                res = res[0] + 1
            else:
                res = res[0]
            return res
    
        def get_path(self):
            '''
            获取当前所在目录的绝对路径
            :return:
            '''
            basepath = self.basedir
            for i in self.path:
                basepath = os.path.join(basepath,i)
            return basepath
    
    
    
    
    
    if __name__ == '__main__':
        c = Client('xie','asd','src',100)
        c.free_size(c.home)
    client.py
    import optparse
    from src import server_handler
    import socketserver
    from conf.setting import *
    
    class ArgvHandler():
        '''
        参数处理类,
        使用optparse.OptionParser来处理参数
        '''
        def __init__(self):
            self.op = optparse.OptionParser()
            options,argv = self.op.parse_args()
            self.verify_argv(options,argv)
    
        def verify_argv(self,options,argv):
            '''
            验证参数函数,通过获取的argv 参数,然后利用hasattr和getattr反射功能来分发功能,比如 start 、help等
            :param options:
            :param argv:
            :return:
            '''
            cmd = argv[0]
    
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func()
            else:
                print("参数错误!")
    
        def start(self):
            '''
            服务启动功能
            :return:
            '''
            print("server is working...")
            s = socketserver.ThreadingTCPServer((IP,PORT), server_handler.FtpHandler)
            s.serve_forever()
    
        def help(self):
            pass
    main.py
    #coding=utf8
    import socketserver
    import struct
    import json,os
    import configparser
    from conf import setting
    from lib import client
    
    class FtpHandler(socketserver.BaseRequestHandler):
        '''
        sockerserver多线程类
        '''
        def ser_recv(self):
            '''
            接受客户端数据
            :return: 返回客户端发给来的字典
            '''
            try:
                length = struct.unpack('i',self.request.recv(4))[0]
                return json.loads(self.request.recv(length).decode('utf8'))
            except Exception as e:
                print(e)
                return False
        def ser_resphone(self,data):
            '''
            给客户端相应的函数
            :param data:
            :return:
            '''
            data = json.dumps(data).encode('utf-8')
            length = struct.pack('i',len(data))
            self.request.send(length)
            self.request.sendall(data)
    
        def handle(self):
            '''
            通信循环,获取客户端发过来的字典,通过字典中的action来判断功能,用hasattr/getattr来分发功能
            :return:
            '''
            while True:
                data = self.ser_recv()
                if not data: break
                if data.get("action") is not None:
                    if hasattr(self,data.get("action")):
                        func = getattr(self,data["action"])
                        func(**data)
                    else:
                        print("无效的操作")
                else:
                    print("无效的操作!")
    
        def auth(self,**data):
            username = data["username"]
            password = data["password"]
            user = self.authenticate(username,password)
            if user:
                res = {
                    "status_code": 254,
                    'bash': self.user.get_bash()
                }
            else:
                res = {
                    "status_code":253,
                }
            self.ser_resphone(res)
    
        def authenticate(self,username,password):
            cfg = configparser.ConfigParser()
            cfg.read(setting.ACCOUNT_PATH)
            if username in cfg.sections() and password == cfg[username]["password"]:
                self.user = client.Client(username,password,cfg[username]['home'],cfg[username]['homemaxsize'])
                return username
    
        def cd(self,**data):
            if data["dirname"] == ".":
                res = {"bash":self.user.get_bash()}
            elif data["dirname"] == "..":
                if len(self.user.path) > 1:
                    self.user.path.pop()
                os.chdir(self.user.get_path())
    
                res = {"bash":self.user.get_bash()}
            else:
                self.user.path.append(data["dirname"])
                os.chdir(self.user.get_path())
                res = {"bash":self.user.get_bash()}
            self.ser_resphone(res)
    
        def ls(self,**data):
            pwd = self.user.get_path()
            if data["dirname"] == "." :
                new_path = pwd
            else:
                new_path = os.path.join(pwd, data["dirname"])
            if (data["dirname"] == ".") or (os.path.exists(new_path)):
                listdir = os.listdir(new_path)
                res = {
                    "status_code":901,
                    "data" : listdir
                }
            else:
                res = {
                    "status_code": 903,
                    "data": "该目录不存在"
                }
    
            self.ser_resphone(res)
    
    
    
    
        def wget(self,**data):
            file_name = os.path.basename(data["filename"])
            file_path = os.path.join(self.user.get_path(),data["filename"])
            if os.path.exists(file_path):
                if os.path.isfile(file_path):
                    self.ser_resphone({"status_code": 901,"filesize":os.path.getsize(file_path)})
                    if self.request.recv(1).decode("utf8") == "1":
                        with open(file_path,'rb') as f:
                            while True:
                                data = f.read(4096)
                                if not data: break
                                self.request.send(data)
                    return
    
            return self.ser_resphone({"status_code":256})
    
    
        def put(self,**data):
            file_name = data['filename']
            file_size = data['filesize']
            if self.user.free < file_size/1024/1024:
                return self.ser_resphone({"status_code": 259})
    
            target_path = data['target_path']
    
            if target_path == '.':
                abs_path = os.path.join(self.user.get_path(),file_name)
            else:
                abs_path = os.path.join(self.user.get_path(),target_path,file_name)
    
            has_received = 0
            if os.path.exists(abs_path):
                file_has_size = os.path.getsize(abs_path)
                if file_has_size < file_size:
                    #断点续传
                    self.ser_resphone({"status_code":800})
                    client_choice = self.ser_recv()
                    if client_choice["choice"] == "Y":
                        self.ser_resphone({"has_received":file_has_size})
                        f = open(abs_path,"ab")
                        has_received += file_has_size
                    else:
                        f = open(abs_path,"wb")
                else:
                    return self.ser_resphone({"status_code":801})
    
            else:
                self.ser_resphone({"status_code":802})
                f = open(abs_path,"wb")
    
    
            while has_received < file_size:
                data = self.request.recv(1024)
                f.write(data)
                has_received += len(data)
    
            f.close()
    
    
    
        def mkdir(self,**data):
            dir_list = data["dirname"]
            for i in dir_list:
                try:
                    os.makedirs(os.path.join(self.user.get_path(),i))
                except FileExistsError as e:
                    print(e)
                    res = {"status_code":902}
                    return self.ser_resphone(res)
    
            res = {"status_code":901}
            self.ser_resphone(res)
    
        def rm_handle(self,**data):
            recv_data = data
            basedir = self.user.get_path()
            if data["action"] == "userdel": basedir = setting.HOME_PATH
            for i in recv_data["dirname"]:
                path = os.path.join(basedir,*data["path"],i)
                print(path)
                if os.path.exists(path):
                    if os.path.isfile(path):
                        os.remove(path)
                    else:
                        data["dirname"] =[i for i in os.listdir(path)]
                        data["path"].append(i)
                        self.rm_handle(**data)
                        data["path"].pop()
                        os.rmdir(path)
                else:
                    return  {"status": 904 ,"dirname":i}
            return {"status": 901}
    
        def rm(self,**data):
            data["path"] = []
            res = self.rm_handle(**data)
            self.ser_resphone(res)
    
    
        def useradd(self,**data):
            config = configparser.ConfigParser()
            code = self.useradd_verify_argv(config,**data)
            if code == 803 or code == 804:return self.ser_resphone({"status_code":code})
            del data["action"]
            config[data["username"]] = data
            if not os.path.exists(os.path.join(setting.HOME_PATH,data['home'])): os.mkdir(os.path.join(setting.HOME_PATH,data['home']))
            with open(setting.ACCOUNT_PATH, 'w') as configfile:
                config.write(configfile)
            self.ser_resphone({"status_code": code})
    
        def useradd_verify_argv(self,conf,**data):
            conf.read(setting.ACCOUNT_PATH)
            if data["action"] == "useradd":
                if data["username"] in conf.sections():
                    return 803
                for i in conf.sections():
                    if data["home"] == conf[i]["home"]:
                        return 804
                return 901
            elif data["action"] == "usermod":
                if data["username"] in conf.sections():
                    if data["password"] is None:
                        data["password"] = conf[data["username"]]["password"]
                    print(data["home"],conf[data["username"]]["home"])
                    if data["home"] != conf[data["username"]]["home"]:
                        for i in conf.sections():
                            if data["home"] == conf[i]["home"]:
                                return 804
                    del data["action"]
                    return 901, data
                else:
                    return 805
            else:
                if data["username"] in conf.sections():
                    return 901
                else:
                    return 805
    
    
        def usermod(self,**data):
            config = configparser.ConfigParser()
            code = self.useradd_verify_argv(config, **data)
            if code == 805 or code == 804 : return self.ser_resphone({"status_code": code})
            if not os.path.exists(os.path.join(setting.HOME_PATH,data['home'])): os.mkdir(os.path.join(setting.HOME_PATH, data['home']))
            config[data["username"]] = code[1]
            with open(setting.ACCOUNT_PATH, 'w') as configfile:
                config.write(configfile)
            self.ser_resphone({"status_code": code[0]})
    
        def userdel(self,**data):
            config = configparser.ConfigParser()
            config.read(setting.ACCOUNT_PATH)
            code = self.useradd_verify_argv(config, **data)
            if code == 805 : return self.ser_resphone({"status_code": code})
            self.ser_resphone({"status_code": code})
            choice = self.request.recv(1).decode('utf8')
            if choice == "Y":
                home = config[data["username"]]["home"]
                self.rm_handle(**{"dirname":[home],"action":"userdel","path":[]})
            config.remove_section(data["username"])
            config.write(open(setting.ACCOUNT_PATH, 'w'))
    server_handler.py

    服务端启动:

     客户端启动:

     ls命令:

     put命令

     断点续传,将s1.avi 上传到 a目录

     上面用ctrl+c中断了上传

     

    cd命令:

     wget命令:

     mkdir命令

    rm命令

     useradd命令

     

     usermod命令

    userdel命令

     断点续传

  • 相关阅读:
    程序员面试金典-整数对查找
    hihocoder-1552-缺失的拼图
    论文: YOLO9000-Better,Faster,Stronger
    hihocoder-1524-逆序对
    hihocoder-1546-集合计数
    hihocoder-1543-SCI表示法
    Oracle中的定时任务JOB
    JS中时间戳处理
    Boostrap小技巧
    Struts标签 logic:iterate简单使用
  • 原文地址:https://www.cnblogs.com/xieys-1993/p/11687176.html
Copyright © 2020-2023  润新知