Python网络编程 03 实验:FTP详解

文章目录

  • 一、小实验FTP程序需求
  • 二、项目文件架构
  • 三、服务端
    • 1、conf/settings.py
    • 2、conf/accounts.cgf
    • 3、conf/STATUS_CODE.py
    • 4、启动文件 bin/ftp_server.py
    • 5、core/main.py
    • 6、core/server.py
  • 四、客户端
    • 1、conf/STATUS_CODE.py
    • 2、bin/ftp_client.py
  • 五、在终端操作示例

一、小实验FTP程序需求

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

二、项目文件架构

在这里插入图片描述

三、服务端

1、conf/settings.py

import os
BASE_DIE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

IP = "127.0.0.1"
PORT = 8000

ACCOUNT_PATH = os.path.join(BASE_DIE, "conf", "accounts.cfg")

2、conf/accounts.cgf

[DEFAULT]

[LuMX]
Password = 123
Quotation = 100

[root]
Password = root
Quotation = 100

3、conf/STATUS_CODE.py

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"
}

4、启动文件 bin/ftp_server.py

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()	# 启动主函数

5、core/main.py

import optparse
import socketserver
from conf.settings import *
from core import server

class ArgvHandler:
    def __init__(self):
        self.op = optparse.OptionParser()
        options, args = self.op.parse_args()

        self.verify_args(args)

    def verify_args(self,args):
        cmd = args[0]

        if hasattr(self, cmd):
            # 方便扩展,例如可以在终端执行 python ftp_server.py start
            func = getattr(self,cmd)
            func()

    def start(self):
        print("the server is working...")
        s = socketserver.ThreadingTCPServer((IP, PORT), server.ServerHandler)
        s.serve_forever()

6、core/server.py

import json,configparser
import os,hashlib

from conf.STATUS_CODE import *
from conf import settings
import socketserver
class ServerHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            data = self.request.recv(1024).strip()
            if not data:
                print("the connect is breaked")
                break
            data = json.loads(data.decode("utf-8"))

            if data.get("action"):  # 字典data存在key为action,且不为空,执行下列代码
                func = getattr(self, data.get("action"))
                func(**data)
            else:
                print("Invalid cmd")

    def send_response(self, state_code):
        response = {"status_code": state_code}
        self.request.sendall(json.dumps(response).encode("utf-8"))

    def auth(self,**data):
        username = data["username"]
        password = data["password"]
        user = self.authenticate(username, password)

        if user:
            self.send_response(254)
        else:
            self.send_response(253)

    def authenticate(self,user,pwd):
        cfg = configparser.ConfigParser()
        cfg.read(settings.ACCOUNT_PATH)

        if user in cfg.sections():
            if cfg[user]["Password"]==pwd:
                self.user = user
                self.mainPath = os.path.join(settings.BASE_DIR,"home",self.user)
                print("passed authentication")
                return user

    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=="Y":
                    self.request.sendall(str(file_has_size).encode("utf-8"))
                    has_received += file_has_size
                    f=open(abs_path,"ab")
                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:
            md5_data = self.request.recv(32)
            f_data = self.request.recv(992)

            if self.md5_check(md5_obj,md5_data,f_data):
                self.send_response(900)
                f.write(f_data)
                has_received += len(f_data)
            else:
                print("md5 check error")
                self.send_response(901)
                break

        f.close()

    def md5_check(self,md5_obj,md5_data,f_data):
        md5_obj.update(f_data)

        if md5_obj.hexdigest().encode("utf-8") == md5_data:
            return True

    def ls(self, **data):
        file_list = os.listdir(self.mainPath)
        file_str="\n".join(file_list)

        if not len(file_list):
            file_str="<empty dir>"
        self.request.sendall(file_str.encode("utf-8"))

    def cd(self,**data):
        dirname=data.get("dirname")

        if dirname == "..":
            self.mainPath=os.path.dirname(self.mainPath)
        else:
            self.mainPath=os.path.join(self.mainPath,dirname)
        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"))

四、客户端

1、conf/STATUS_CODE.py

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"
}

2、bin/ftp_client.py

import os, sys, hashlib
import optparse,socket,json

PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)

from conf.STATUS_CODE import STATUS_CODE

class ClientHandler():
    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.make_connection()
        self.mainPath=os.path.dirname(os.path.abspath(__file__))

    def verify_args(self,options):
        '''
        验证端口号是否合法
        '''
        port = options.port

        if 0 <= int(port) <= 65535:
            return True
        else:
            exit("the port is not in 0-65535")

    def make_connection(self):
        '''
        处理链接
        '''
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.sock.connect((self.options.server, int(self.options.port)))

    def interactive(self):
        '''
        处理通讯
        '''
        print("begin to interactive...")

        if self.authenticate():
            while True:
                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 put(self,*cmd_list):
        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.send(json.dumps(data).encode("utf-8"))
        is_exist=self.sock.recv(1024).decode("utf-8")

        has_sent=0

        if is_exist=="800":
            # 文件不完整
            choice=input("the file exist,but not enough,do 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_exist=="801":
            # 文件完全存在
            print("the file exist")
            return

        f = open(local_path,"rb")
        f.seek(has_sent)
        md5_obj = hashlib.md5()
        while has_sent < file_size:
            f_data = f.read(992)

            if self.md5_check(md5_obj,f_data):
                has_sent += len(f_data)
                self.show_progress(has_sent,file_size)
            else:
                print("md5 check is error!")
                break

        f.close()

        if has_sent == file_size:
            print("\n",end="")
            print("put success!")

    def md5_check(self,md5_obj,f_data):
        md5_obj.update(f_data)
        md5_data = md5_obj.hexdigest().encode("utf-8")
        data = md5_data + f_data
        self.sock.sendall(data)
        response = self.response()

        if response["status_code"] == 900:
            return True

    def show_progress(self,has,total):
        '''
        显示进度条
        '''
        rate=int(float(has)/float(total)*10000)/10000
        rate_num=int(rate*100)
        sys.stdout.write("{:.2%} {}\r".format(rate,rate_num*"#"))
        # \r 表示光标移动到行首

    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):
        data={
            "action": "cd",
            "dirname": cmd_list[1]
        }
        self.sock.sendall(json.dumps(data).encode("utf-8"))

        data = self.sock.recv(1024).decode("utf-8")
        print(os.path.basename(data))
        self.current_dir=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)

    def authenticate(self):
        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,user,pwd):
        data = {
            "action":"auth",
            "username":user,
            "password":pwd
        }

        self.sock.send(json.dumps(data).encode("utf-8"))
        response=self.response()
        print("response:",response["status_code"])
        if response["status_code"]==254:
            self.user = user
            self.current_dir=user
            print(STATUS_CODE[254])
            return True
        else:
            print(STATUS_CODE[response["status_code"]])

ch = ClientHandler()
ch.interactive()

五、在终端操作示例

服务端
在这里插入图片描述
客户端
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/606857.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

爬虫学习--5.xpath数据解析

xpath是XML路径语言&#xff0c;它可以用来确定xml文档中的元素位置&#xff0c;通过元素路径来完成对元素的查找。HTML就是XML的一种实现方式&#xff0c;所以xpath是一种非常强大的定位方式。 基本概念 XPath&#xff08;XML Path Language&#xff09;是一种XML的查询语言…

Linux学习笔记3---WSL2交叉编译

ARM 裸机、Uboot 移植、Linux 移植这些都需要在 Ubuntu 下进行编译&#xff0c;编译就需要编译器&#xff0c;在上一章里面已经讲解了如何在 Liux 进行 C 语言开发&#xff0c;里面使用 GCC 编译器进行代码编译&#xff0c;但使用的 gcc 编译器是针对 X86 架构的&#xff01;而…

小程序开发之tdesignUI组件的简易使用教程

文章目录 TDesign简介小程序端使用TDesign一、安装二、使用可能的问题 附&#xff1a;如何使用weui开发参考链接 TDesign简介 TDesign 是腾讯各业务团队在服务业务过程中沉淀的一套企业级设计体系。 该UI框架支持桌面端、移动端、小程序端等全端。 小程序端使用TDesign 开发…

危化工厂安全监管新篇章:智能化视频监控方案的探索与实践

一、背景需求分析 危化品因其特殊的物理和化学性质&#xff0c;一旦发生事故&#xff0c;往往会造成严重的人员伤亡和财产损失。传统的监管手段往往依赖于人工巡查和定期检测&#xff0c;然而这种方式不仅效率低下&#xff0c;而且难以全面覆盖和实时监控。因此&#xff0c;借…

8.删除有序数组中的重复项 II

文章目录 题目简介题目解答解法一&#xff1a;双指针&#xff08;快慢指针&#xff09;代码&#xff1a;复杂度分析&#xff1a; 题目链接 大家好&#xff0c;我是晓星航。今天为大家带来的是 删除有序数组中的重复项 II 相关的讲解&#xff01;&#x1f600; 题目简介 题目解…

HCIA考试通过率100%|广东理工学院30名学子顺利通过华为HCIA认证

近日&#xff0c;讯方技术与广东理工学院联合创建的华为ICT学院&#xff0c;共有30名学生成功通过在广州举行的华为HCIA认证考试&#xff0c;并获得了华为HCIA认证证书。其中&#xff0c;19名学生成功通过HCIA-Cloud Computing认证&#xff0c;11名学生成功通过HCIA-Datacom认证…

Vue3 学习 (一)

编码规范 Vue3 通常和 typescript 结合使用 采用 TypeScript 组合式 api setup 语法糖 的形式 一、Vue3 简介 和 Vue2 相比 性能得到提升 源码升级 使用 Proxy 代替 definedProperty 实现响应式 重写虚拟 DOM 的实现和Tree-Shaking 可以更好的支持 TypeScript 新的…

Python——Fastapi管理平台(打包+优化)

目录 一、配置多个表 1、后端项目改造 2、导包报错——需要修改&#xff08;2个地方&#xff09; 3、启动后端&#xff08;查看是否有问题&#xff09; 4、配置前端 二、打包——成exe文件&#xff08;不包含static文件&#xff09;简单 1、后端修改 2、前端修改 3、运行打包命…

5 Spring 事务管理

目录 1.概述 2.事务特性&#xff1a;ACID 3.Spring 框架的事务管理支持两种方式 编程式事务 申明式事务 4.Spring 事务管理 API 事务管理器接口 Spring 的回滚方式 事务定义接口 事务的四种隔离级别 事务的七种传播行为 5.事务注解例子&#xff1a; Transactianal…

中间件之异步通讯组件RabbitMQ进阶

这里我们必须尽可能确保MQ消息的可靠性&#xff0c;即&#xff1a;消息应该至少被消费者处理1次 那么问题来了&#xff1a; 我们该如何确保MQ消息的可靠性&#xff1f; 如果真的发送失败&#xff0c;有没有其它的兜底方案&#xff1f; 首先&#xff0c;我们一起分析一下消息…

多模态大模型MLLM VIT CLIP BLIP

一、Vit模型介绍 Vit&#xff08;Vision Transformer&#xff09;即将Transformer应用于视觉领域。 Transformer输入输出都是一个序列&#xff0c;若需要应用于视觉领域&#xff0c;则需要考虑如何将一个2d图片转化为一个1d的序列&#xff0c;最直观的想法将图片中的像素点输…

Logit Standardization in Knowledge Distillation 知识蒸馏中的logit标准化

摘要 知识蒸馏涉及使用基于共享温度的softmax函数将软标签从教师转移到学生。然而&#xff0c;教师和学生之间共享温度的假设意味着他们的logits在logit范围和方差方面必须精确匹配。这种副作用限制了学生的表现&#xff0c;考虑到他们之间的能力差异&#xff0c;以及教师天生…

旺店通·企业奇门和金蝶云星空单据接口对接

旺店通企业奇门和金蝶云星空单据接口对接 源系统:金蝶云星空 金蝶K/3Cloud&#xff08;金蝶云星空&#xff09;是移动互联网时代的新型ERP&#xff0c;是基于WEB2.0与云技术的新时代企业管理服务平台。金蝶K/3Cloud围绕着“生态、人人、体验”&#xff0c;旨在帮助企业打造面向…

Web3 ETF软件开发

开发Web3 ETF软件涉及到金融、法律和技术等多个领域的专业知识&#xff0c;因此存在以下技术难点&#xff0c;开发Web3 ETF软件是一项复杂的技术挑战&#xff0c;需要综合考虑各种因素。开发人员需要具备较强的技术能力和跨学科知识才能成功开发Web3 ETF软件。北京木奇移动技术…

解决$‘\r‘: command not found 或syntax error near unexpected token `$‘\r‘的四个方法

问题原因&#xff1a; 两个报错原因都是Linux和windows下的回车换行符不兼容 解决方法&#xff1a; 方法一&#xff1a;在windows系统可以用文本编辑器查看所有字符&#xff0c;例如notepad&#xff0c;编辑->档案格式转换->转换为UNIX格式 方法二&#xff1a;在Linux系…

uniapp读取项目本地文件/json文件/txt文件

uniapp读取项目本地文件/json文件/txt文件 文件必须放在static目录下 方法&#xff1a; /*** 访问static里面的文件* param url 文件路径 必须在static目录下*/ function localFetch(url) {return new Promise((resolve, reject) > {plus.io.resolveLocalFileSystemURL(_ww…

【LeetCode】链表oj专题

前言 经过前面的学习&#xff0c;咋们已经学完了链表相关知识&#xff0c;这时候不妨来几道链表算法题来巩固一下吧&#xff01; 如果有不懂的可翻阅之前文章哦&#xff01; 个人主页&#xff1a;小八哥向前冲~-CSDN博客 数据结构专栏&#xff1a;数据结构【c语言版】_小八哥…

HCIP-Datacom-ARST必选题库_OSPF【道题】

某工程师利用2台路由器进行IPv6测试&#xff0c;他想要通过运行OSPFv3实现IPv6网络的互联互通。关于R1需要进行的OSPPv3相关配置&#xff0c;正确的有? [R1] router id 10.1.1.1A [R1-Giqabi tEthernet0/0/1] ospfv3 1 area 0 [R1-ospfv3-11 router-id 10.1.1.1 [R1-ospfv3…

AcWing 835:Trie字符串统计 ← 字典树(Trie树)模板题

【题目来源】https://www.acwing.com/problem/content/837/【题目描述】 维护一个字符串集合&#xff0c;支持两种操作&#xff1a; ● I x 向集合中插入一个字符串 x&#xff1b; ● Q x 询问一个字符串在集合中出现了多少次。 共有 N 个操作&#xff0c;所有输入的字符…

【JAVA】类加载过程,以及类加载器

类加载过程&#xff0c;以及类加载器 一、类加载的过程二、类加载器介绍三、跨类加载三、举例说明 一、类加载的过程 类加载是Java虚拟机&#xff08;JVM&#xff09;将类文件加载到内存中并转换成对应的类对象的过程。它确保了类文件能够正确加载并转换成可执行的类对象&…
最新文章