博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python之Ftp作业(断点续传未完成)
阅读量:5797 次
发布时间:2019-06-18

本文共 15562 字,大约阅读时间需要 51 分钟。

作业需求

要求:

  1. 用户加密认证 1
  2. 允许同时多用户登录 1
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录 1
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录 1
  6. 允许用户查看当前目录下文件 1
  7. 允许上传和下载文件,保证文件一致性 1
  8. 文件传输过程中显示进度条 1
  9. 附加功能:支持文件的断点续传---------------------未完成

作业分析

作业结构:

client:

server:

小说明:

 

在这个作业中呢,用到了很多知识点,比如用户加密认证加密传送文件都用到了hashlib模块;保存用户信息,传递一些字典用到了json模块;多用户同时在线用到了socketserver模块;还有一些命令的操作,用到了os.popen()函数;还有一些有关路径的函数,什么的。。。诶,好像也没用到好多哈。。。对了还有反射,这个非常重要。其他就没什么的了,,具体的分析看下面的一个网站:

精彩内容:

client端代码

import socket,hashlib,json,os ,sysbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))print(base_dir)sys.path.append(base_dir) # 添加conf文件的路径\\from conf import configfrom core import put, auth, ls, cd, mkdirhttpCode = """"200" : "服务器成功返回网页""404" : "请求的网页不存在""403" : "出现错误""503" : "服务不可用" """class FtpClient(object):    def __init__(self):        self.client = socket.socket()   # 创建一个socket客户端        self.userMessDirt = {}    def connect(self,host,port):        self.client.connect((host, port))    # 客户端和服务器端进行连接    def help(self):        msg = '''                ls                pwd                cd ../..                get filename                put filename                '''        print(msg)    def interactive(self):        self.userMessDirt = json.loads(self.client.recv(config.buffer).decode())        self.localDir = os.path.basename(self.userMessDirt["homeDir"])  # 初始地址        while True:            cmd_str = input("[%s@user %s] #" %(self.userMessDirt["userName"], self.localDir))            if len(cmd_str) ==0:continue            cmd = cmd_str.split()[0]            if hasattr(self,"cmd_%s" % cmd):                func = getattr(self, "cmd_%s" % cmd)                func(cmd_str)            else:                self.help()    def cmd_ls(self, *args):        '''客户端实现打印出该目录下的所有文件'''        ls.ls(self, *args)    def cmd_pwd(self, *args):        '''客户端实现打印出当前目录功能'''        pass    def cmd_cd(self, *args):        '''客户端实现切换目录功能'''        cd.cd(self, *args)    def cmd_get(self, *args):        '''客户端下载文件'''        pass    def cmd_put(self, *args):        '''客户端上传文件'''        put.cmd_put(self,*args)    def cmd_mkdir(self, *args):        '''创建一个空文件夹'''        mkdir.mkdir(self, *args)    def auth_login(self):        auth.auth_login(self)    def auth_register(self):        auth.auth_register(self)def run():    info = """    1.登录    2.注册    >>:"""    auth_choice = input(info).strip()    ftp = FtpClient()    ftp.connect("localhost",9090)    if auth_choice == "1":        ftp.auth_login()    else:        ftp.auth_register()    ftp.interactive()
main
import hashlib, os, sysbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))from conf import configdef auth_login(self):    self.client.send("login".encode("utf-8"))  # 通知对面我要登录了    loginErrorCount = 3    while True:        userName_input = self.client.recv(config.buffer)        userName = input(userName_input.decode()).strip()        self.client.send(userName.encode("utf-8"))  # 注意这里的编码问题        httpCode = self.client.recv(config.buffer).decode()        if httpCode == "403":            print("用户名错误,请重新输入")        elif httpCode == "200":            break    while loginErrorCount:        has = hashlib.md5()        passWord_input = self.client.recv(config.buffer)        passWord = input(passWord_input.decode()).strip()        has.update(passWord.encode("utf-8"))  # 使用hashlib编码必须要使用byte类型        self.client.send(has.hexdigest().encode("utf-8"))        # print("has:",has.hexdigest())             # 测试是否生成了md5        # self.client.send(self.passWord.encode("utf-8"))        http_code = self.client.recv(config.buffer).decode()        if http_code == "200":            break        elif http_code == "403":            loginErrorCount -= 1            print("密码错误,剩余\033[31;1m%s\033[0m机会" % loginErrorCount)    if loginErrorCount == 0:        sys.exit(0)def auth_register(self):    self.client.send("register".encode("utf-8"))  # 通知对面我要注册了    has = hashlib.md5()    has_1 = hashlib.md5()    userName_input = self.client.recv(config.buffer)    userName = input(userName_input.decode()).strip()    self.client.send(userName.encode("utf-8"))    while True:        passWord_input = self.client.recv(config.buffer)        passWord = input(passWord_input.decode()).strip()        has.update(passWord.encode("utf-8"))  # 对第一次输入的密码加密        self.client.send(has.hexdigest().encode("utf-8"))        passWord_input_again = self.client.recv(config.buffer)        passWord_again = input(passWord_input_again.decode()).strip()        has_1.update(passWord_again.encode("utf-8"))  # 对第一次输入的密码加密        self.client.send(has_1.hexdigest().encode("utf-8"))        http_code = self.client.recv(config.buffer).decode()        if http_code == "200":            break        elif http_code == "403":            print("密码错误。。。")
client.auth
import json, osbase_dir = os.path.dirname(os.path.abspath(__file__))from conf import configdef cd(self, *args):    '''cd 中.. 是返回上层目录,/ '''    cmd_str = args[0].split()    if len(cmd_str) > 1:        cmd_path = cmd_str[1]        cmd_dirt = {            "action" : "cd",            "path" : cmd_path        }        self.client.send(json.dumps(cmd_dirt).encode("utf-8"))        self.localDir = os.path.basename(self.client.recv(config.buffer).decode())
client.cd

ps:在这里说一下,貌似真的是各种命令的实行大多一样,只要做出来了一个,其他的就能做了

import sysdef progress_bar(num_cur, total):    ratio = float(num_cur) / float(total)    percentage = int(ratio * 100)    r = '\r\n[%s%s]%d%%' % (">"*percentage, " "*(100-percentage), percentage )    sys.stdout.write(r)    sys.stdout.flush()
client.progress_bar
import os, json, hashlib, sysbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))from core import progress_barfrom conf import configdef cmd_put(self, *args):    cmd_str = args[0].split()  # 提取出args中的数据,并将其按空格的形式转换成列表    if len(cmd_str) > 1:        fileName = cmd_str[1]        if os.path.isfile(fileName):  # 判断这是不是个文件            fileSize = os.stat(fileName).st_size  # 获得文件的大小            cmd_dirt = {                "action": "put",                "fileName": fileName,                "fileSize": fileSize,                "overridden": True            }            self.client.send(json.dumps(cmd_dirt).encode("utf-8"))  # 用json将整个字典传过去            self.client.recv(config.buffer)  # 等待服务器端的确认, 不用做任何处理            with open(fileName, "rb") as fr:                has = hashlib.md5()                sum_size = 0                for line in fr:                    line_size = len(line)                    sum_size += line_size                    has.update(line)                    self.client.send(has.hexdigest().encode("utf-8"))  # line 本身就是字节类型                    return_md5 = self.client.recv(config.buffer)                    if has.hexdigest() == return_md5.decode():                        self.client.send(b'200')                        self.client.recv(config.buffer)                        self.client.send(line)                        progress_bar.progress_bar(sum_size, cmd_dirt["fileSize"])                    else:                        self.client.send(b'403')                        print("传输错误。。。")                        break                else:                    print("\nfile upload success...")        else:            print(fileName, "不存在。。。")
client.put
# lsimport json, osbase_dir = os.path.dirname(os.path.abspath(__file__))from conf import configdef ls(self, *args):    cmd_str = args[0].split()    if len(cmd_str) == 1 and cmd_str[0] == "ls":        cmd_dirt = {            "action" : "ls",        }        self.client.send(json.dumps(cmd_dirt).encode("utf-8"))        return_mess = self.client.recv(config.buffer)        print(return_mess.decode())# mkdirimport json, osbase_dir = os.path.dirname(os.path.abspath(__file__))from conf import configdef mkdir(self, *args):    cmd_str = args[0].split()    if len(cmd_str) > 1:        fileName = cmd_str[1]        cmd_dirt = {            "action" : "mkdir",            "fileName" : fileName,        }        self.client.send(json.dumps(cmd_dirt).encode("utf-8"))        self.client.recv(config.buffer)
ls & mkdir(pwd、get都没做)
import os,sys,logging# 最顶端的路径BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))# 存放日志的目录LOG_DIR = BASE_DIR + '/log/log.txt'logging.basicConfig(level=logging.DEBUG,    # level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',                    datefmt='%a, %d %b %Y %H:%M:%S',                    filename=LOG_DIR,                    filemode='a+')# 存放用户信息的目录USER_DIR = BASE_DIR + '/date/'# 每次发送文件的大小buffer = 2048# 给用户分配的内存大小memory = 100 * 1024 * 1024
client.config

server代码

import socketserver,os,sys,jsonbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))print(base_dir)sys.path.append(base_dir ) # 添加conf文件的路径from conf import configfrom core import put, auth, ls, cd, mkdirclass MyTCPHandler(socketserver.BaseRequestHandler):    def handle(self):       # 所有的请求都是在handle 中执行的        try:            self.userMessDirt = {}            state = self.request.recv(config.buffer).decode()            if state == "login":                self.auth_login()            elif state == "register":                self.auth_register()            self.request.send(json.dumps(self.userMessDirt).encode("utf-8"))            self.current_path = os.path.join(self.userMessDirt["homeDir"])            os.chdir(self.current_path)                                 # 将当前的目录改到self.current_path下面去,这里是指每个用户的root目录            while True:                cmd_dirt = json.loads(self.request.recv(config.buffer).decode())                action = cmd_dirt["action"]                if hasattr(self, action):                    func = getattr(self, action)                    func(cmd_dirt)        except ConnectionResetError as e:            print(self.userMessDirt["userName"], "断开。。。")    def ls(self, *args):        '''查看该目录下的所有文件'''        ls.ls(self, *args)    def pwd(self, *args):        '''打印当前路径'''        pass    def cd(self, *args):        '''切换目录'''        cd.cd(self,*args)    def get(self, *args):        '''服务器端 发送文件给 客户端'''        pass    def mkdir(self, *args):        '''创建一个空的文件夹'''        mkdir.mkdir(self, *args)    def put(self, *args):        '''服务器端 接收 客户端给的文件'''        put.put(self, *args)    def auth_login(self):        auth.auth_login(self)    def auth_register(self):        auth.auth_register(self)def run():    Host , Port = "localhost" , 9090        # ip地址    server = socketserver.ThreadingTCPServer((Host,Port) , MyTCPHandler)    # 每来一个请求,服务端就开启一个新的线程    server.serve_forever()
main
import os,sys,logging# 最顶端的路径BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))# 存放日志的目录LOG_DIR = BASE_DIR + '/log/log.txt'logging.basicConfig(level=logging.DEBUG,    # level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',                    datefmt='%a, %d %b %Y %H:%M:%S',                    filename=LOG_DIR,                    filemode='a+')# 存放用户信息的目录USER_DIR = BASE_DIR + '/date/'# 每次发送文件的大小buffer = 2048# 给用户分配的内存大小memory = 100 * 1024 * 1024# 家目录的路径HOME_DIR = BASE_DIR + '\home\\'
server.config
import os, sys, jsonbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))from conf import configdef dirt(userName, passWord, memory, homeDir):    return {        "userName":userName,        "passWord":passWord,        "memory" : memory,        "homeDir" : homeDir    }def auth_login(self):    while True:        self.request.send('请输入用户名:'.encode("utf-8"))        userName = self.request.recv(config.buffer)        try:            with open(config.USER_DIR + userName.decode(), "r", encoding="utf-8") as fr:                self.userMessDirt = json.load(fr)  # 记住json只能dump&load一次,所以放在循环里的时候注意            self.request.send(b'200')            break        except FileNotFoundError as e:            self.request.send(b'403')    loginErrorCount = 3    while loginErrorCount:        self.request.send('请输入密码:'.encode("utf-8"))        passWord_md5 = self.request.recv(config.buffer)        # print("md5:",passWord_md5.decode())        if self.userMessDirt["passWord"] == passWord_md5.decode():            self.request.send(b"200")            break        else:            self.request.send(b"403")            loginErrorCount -= 1    if loginErrorCount == 0:        sys.exit(0)def auth_register(self):    self.request.send('请输入用户名:'.encode("utf-8"))    userName = self.request.recv(config.buffer).decode()  # 一定要记得解码解码    while True:        self.request.send('请输入密码:'.encode("utf-8"))        passWord_md5 = self.request.recv(config.buffer).decode()        self.request.send('请再次输入密码:'.encode("utf-8"))        passWord_again_md5 = self.request.recv(config.buffer).decode()        if passWord_md5 == passWord_again_md5:            self.request.send(b'200')            os.makedirs(config.HOME_DIR + userName + "\\root\\")  # 为用户创建家目录            self.userMessDirt = dirt(userName, passWord_md5, config.memory, config.HOME_DIR + userName + "\\root")            with open(config.USER_DIR + userName, "w") as fw:                json.dump(self.userMessDirt, fw)  # 将用户的信息存到文件中            break        else:            self.request.send(b'403')
server.auth
# cdimport osdef cd(self, *args):    path_list = self.current_path.split("\\")    cmd_str = args[0]    cmd_path = cmd_str["path"]    if cmd_path == "/":        path_list_len = len(path_list)        while True:            if path_list[path_list_len - 1] == self.userMessDirt["userName"]:                break            else:                path_list.pop()                path_list_len -= 1    elif cmd_path == "..":        path_list.pop()    else:        cmd_path_list = cmd_path.split("\\")        path_list += cmd_path_list    cmd_current_path = "\\".join(path_list)    if os.path.exists(cmd_current_path):        self.current_path = cmd_current_path        os.chdir(self.current_path)    print(self.current_path)    self.request.send(self.current_path.encode("utf-8"))# lsimport osdef ls(self, *args):    cmd_str = args[0]    mess = os.popen("dir" ).read()    self.request.send(mess.encode("utf-8"))# mkdirimport osdef mkdir(self, *args):    cmd_str = args[0]    fileName = cmd_str["fileName"]       # 需要创建的文件名字    os.popen("mkdir %s" %fileName)    self.request.send(b'200')
cd & ls &mkdir
import os, jsonbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))from conf import configdef put(self, *args):    cmd_dirt = args[0]    fileName = cmd_dirt["fileName"]    fileSize = cmd_dirt["fileSize"]    overridden = cmd_dirt["overridden"]    if overridden:        fw = open(fileName, "wb")    else:        fw = open(fileName + '.new', 'wb')    self.request.send(b"200")  # 确认准备好接收文件了    fileReceive = 0    while fileSize > fileReceive:        date_md5 = self.request.recv(config.buffer)        self.request.send(date_md5)        http_code = self.request.recv(config.buffer).decode()        if http_code == "200":            self.request.send(b"ok")            if (fileSize - fileReceive) > config.buffer:                date = self.request.recv(config.buffer)            else:                date = self.request.recv(fileSize - fileReceive)            fw.write(date)            fileReceive += len(date)        elif http_code == "403":            print("文件错误。。。")            break    else:        fw.close()        print(fileName, "接收完毕。。。")
server.put

其他的也没什么可说的了,这个作业真心做了好几天,多亏了上面的博主发的博客,才有点头绪能够写下去

转载于:https://www.cnblogs.com/ouyang1124/p/10568218.html

你可能感兴趣的文章
微软的云策略
查看>>
Valid Parentheses
查看>>
【ES6】数值的扩展
查看>>
性能测试之稳定性测试
查看>>
ES6的 Iterator 遍历器
查看>>
2019届高二(下)半期考试题(文科)
查看>>
【REDO】删除REDO LOG重做日志组后需要手工删除对应的日志文件(转)
查看>>
nginx 301跳转到带www域名方法rewrite(转)
查看>>
AIX 配置vncserver
查看>>
windows下Python 3.x图形图像处理库PIL的安装
查看>>
【IL】IL生成exe的方法
查看>>
network
查看>>
SettingsNotePad++
查看>>
centos7安装cacti-1.0
查看>>
3个概念,入门 Vue 组件开发
查看>>
没有JS的前端:体积更小、速度更快!
查看>>
数据指标/表现度量系统(Performance Measurement System)综述
查看>>
GitHub宣布推出Electron 1.0和Devtron,并将提供无限制的私有代码库
查看>>
Angular2, NativeScript 和 React Native比较[翻译]
查看>>
论模式在领域驱动设计中的重要性
查看>>