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

news2024/10/5 16:27:08

文章目录

  • 一、小实验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.coloradmin.cn/o/1658311.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CTF-Crypto】RSA-选择明密文攻击 一文通

RSA&#xff1a;选择明密文攻击 关于选择明/密文攻击&#xff0c;其实这一般是打一套组合拳的&#xff0c;在网上找到了利用的思路&#xff0c;感觉下面这个题目是真正将这个问题实现了&#xff0c;所以还是非常棒的一道题&#xff0c;下面先了解一下该知识点&#xff1a;(来自…

Telegram Premium会员有什么功能?

Telegram Premium 是 Telegram 的付费版本&#xff0c;现已上线。付费版本被定位为帮助运行平台并进一步开发它的一种方式。但除此之外&#xff0c;它对你有什么好处&#xff1f;以下是Telegram Premium中包含的八个主要功能&#xff1a; 1.没有广告。 Telegram Premium是无广…

李飞飞团队 AI4S 最新洞察:16 项创新技术汇总,覆盖生物/材料/医疗/问诊……

不久前&#xff0c;斯坦福大学 Human-Center Artificial Intelligence (HAI) 研究中心重磅发布了《2024年人工智能指数报告》。 作为斯坦福 HAI 的第七部力作&#xff0c;这份报告长达 502 页&#xff0c;全面追踪了 2023 年全球人工智能的发展趋势。相比往年&#xff0c;扩大了…

[Linux]如何在Ubuntu 22.04系統安裝Node-red?

Node-red是一個建立在Node.js上的視覺化程式設計工具&#xff0c;其常見的應用情境為建置或轉換各項硬體之間的通信協定的物聯網或工聯網場域&#xff0c;其可藉由設置來安裝第三方應用模組來建置多樣的通信協定節點&#xff0c;包含modbus in/out, mqtt in/out, websocket in/…

Java数组创建与使用

一.创建和初始化 1.数组是怎么创建的&#xff1f; 直接举例子&#xff1a; int[] arr new int[10]; 这里只简单的举一个int开辟数组的例子。 可见java数组的创建于C语言是不同的。前面是一个int[ ]就是一个数组的数据类型&#xff0c;后面的arr是数组名&#xff0c;最后[…

【代码随想录——栈与队列】

1.栈和队列理论基础 栈和队列的原理大家应该很熟悉了&#xff0c;队列是先进先出&#xff0c;栈是先进后出。 2.用栈实现队列 type MyQueue struct {head []intheadSize intstore []intstoreSize int }func Constructor() MyQueue {return MyQueue{head : make([]int,100),h…

《ESP8266通信指南》11-Lua开发环境配置

往期 《ESP8266通信指南》10-MQTT通信&#xff08;Arduino开发&#xff09;-CSDN博客 《ESP8266通信指南》9-TCP通信&#xff08;Arudino开发&#xff09;-CSDN博客 《ESP8266通信指南》8-连接WIFI&#xff08;Arduino开发&#xff09;&#xff08;非常简单&#xff09;-CSD…

qt for android 无法进入调试c++代码解决方法

1.上一篇文章介绍了qt 5.15.13配合NDK25 的解决方法&#xff0c;但是还存在一个问题是可以调试qml代码但是无法进入c代码中。 在网上进行搜索&#xff0c;得到的答案是确实存在这个问题&#xff0c;包括这个qt6版本。 2.看来这个问题没有办法了&#xff0c;静下来思考下。这个…

AI助力临沂商贸的世界语言

“你好”“Bonjour”“Hola”……“中国老板娘”能够流利运用 17 种语言&#xff0c;近日在海外爆火。借由 AI 技术的助力&#xff0c;商家在镜头前自如切换各国语言&#xff0c;与来自全球各地的外商实现无障碍的沟通交流。这种现象背后&#xff0c;是 AI 技术对传统商贸模式的…

Python 控制 Keysight (原Agilent) 直流电源

前言 直流电源是如图型号,是keysight 6631系列;由于本身直流电源的功能也不复杂,所以控制起来相对比较简单,本来不想写这篇文章的,但是想想还是做一个简单的记录吧! 硬件环境结构图: 通信方式:由于该直流电源只GPIB接口,所以是通过GPIB线与设备进行通信 实现原理: …

中国网安上市公司2023财报摘要及启示

随着国内网络安全市场越来越来越来越卷&#xff0c;出海&#xff0c;从原来的陌生遥远不看好&#xff0c;已经成为许多厂商不得不思考的一个新方向。 阻力不变。 地缘政治问题依然存在&#xff0c;沟通成本高&#xff0c;对产品成熟度要求高&#xff0c;对团队背景和公司整体能…

如何通过OMS加快大表迁移至OceanBase

OMS&#xff0c;是OceanBase官方推出的数据迁移工具&#xff0c;能够满足众多数据迁移场景的需求&#xff0c;现已成为众多用户进行数据迁移同步的重要工具。OMS不仅支持多种数据源&#xff0c;还具备全量迁移、增量同步、数据校验等功能&#xff0c;并能够对分表进行聚合操作&…

文件操作

前言&#xff1a; 文件内容属性 要向访问文件就要打开文件——>用进程来打开——>要把文件先加载到内存中——> 一个进程可以打开多个文件&#xff0c;OS中也有可能多个进程打开了多个文件 文件以多&#xff0c;就需要进行管理&#xff0c;——先描述再组织 没有被打开…

基于Spring Boot框架实现大学生选课管理系统

文章目录 源代码下载地址项目介绍项目功能界面预览 项目备注源代码下载地址 源代码下载地址 点击这里下载源码 项目介绍 项目功能 教务处管理 开课、开班审批&#xff0c;排课处理&#xff0c;班级操作&#xff0c;选课时间段管理** 使用了sql解决了开课开班的时间段的冲突…

基于PHP后台微信图书馆借书还书小程序系统设计与实现

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

IDM下载器激活

文章目录 1、Internet Download Manager简介2、Internet Download Managery应用3、Internet Download Managery下载 1、Internet Download Manager简介 Internet Download Manager (IDM) 是一款功能强大的下载管理软件&#xff0c;旨在帮助用户更高效地管理和加速其下载任务。它…

【硬件开发】原型设计对于成功产品开发重要性及常见阶段

电子产品的设计与开发始于原型制作阶段。这些原型虽可能与最终产品极为相似&#xff0c;但总带有实验性质&#xff0c;因为电子原型的制作过程包括对新概念、新思想及新解决方案的测试。虽然存在出错的风险&#xff0c;跳过这一阶段可能会导致不必要的开支。不擅长电子硬件设计…

CSS和JavaScript

CSS 在html中引入CSS 我们需要先在该项目先建立css文件 html引入CSS,在<head></head>中添加<link>标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" co…

QT--4

QT 使用定时器完成闹钟 #include "widget.h" #include "ui_widget.h"void Widget::timestart() {timer.start(1000); }void Widget::timeend() {timer.stop(); }Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(t…

电商核心技术揭秘52:数字化内容营销创新

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘相关系列文章合集&#xff08;3&#xff09; 电商技术揭秘四十一&#xff1a;电商平台的营销系统浅析 电商技术揭秘四十二&#…