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 = 8000ACCOUNT_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 mainif __name__ == "__main__":main.ArgvHandler() # 启动主函数
5、core/main.py
import optparse
import socketserver
from conf.settings import *
from core import serverclass 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 startfunc = 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,hashlibfrom 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")breakdata = 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 = userself.mainPath = os.path.join(settings.BASE_DIR,"home",self.user)print("passed authentication")return userdef 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=0if os.path.exists(abs_path):file_has_size=os.stat(abs_path).st_sizeif 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_sizef=open(abs_path,"ab")else:f=open(abs_path,"wb")else:# 文件存在,且完整self.request.sendall("801".encode("utf-8"))returnelse:# 文件不存在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)breakf.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 Truedef 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,jsonPATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)from conf.STATUS_CODE import STATUS_CODEclass 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.portif 0 <= int(port) <= 65535:return Trueelse: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_listlocal_path=os.path.join(self.mainPath,local_path)file_name=os.path.basename(local_path)file_size=os.stat(local_path).st_sizedata = {"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=0if 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")returnf = 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!")breakf.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_dataself.sock.sendall(data)response = self.response()if response["status_code"] == 900:return Truedef show_progress(self,has,total):'''显示进度条'''rate=int(float(has)/float(total)*10000)/10000rate_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 datadef 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 = userself.current_dir=userprint(STATUS_CODE[254])return Trueelse:print(STATUS_CODE[response["status_code"]])ch = ClientHandler()
ch.interactive()
五、在终端操作示例
服务端

客户端


相关文章:
Python网络编程 03 实验:FTP详解
文章目录 一、小实验FTP程序需求二、项目文件架构三、服务端1、conf/settings.py2、conf/accounts.cgf3、conf/STATUS_CODE.py4、启动文件 bin/ftp_server.py5、core/main.py6、core/server.py 四、客户端1、conf/STATUS_CODE.py2、bin/ftp_client.py 五、在终端操作示例 一、小…...
个人银行账户管理程序(2)
在(1)的基础上进行改进 1:增加一个静态成员函数total,记录账户总金额和静态成员函数getTotal 2对不需要改变的对象进行const修饰 3多文件实现 account。h文件 #ifndef _ACCOUNT_ #define _ACCOUNT_ class SavingAccount {pri…...
2024.04.19校招 实习 内推 面经
绿*泡*泡VX: neituijunsir 交流*裙 ,内推/实习/校招汇总表格 1、校招&转正实习 | 美团无人机业务部招聘(内推) 校招&转正实习 | 美团无人机业务部招聘(内推) 2、校招&实习 | 快手 这些岗位…...
Python并发编程 04 进程与线程基础
文章目录 一、操作系统简介二、进程三、线程四、线程的调用1、示例2、join方法3、setDaemon方法4、继承式调用(不推荐)5、其他方法 一、操作系统简介 ①操作系统是一个用来协调、管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用…...
模板引擎Freemarker
什么是模板引擎 根据前边的数据模型分析,课程预览就是把课程的相关信息进行整合,在课程预览界面进行展示,课程预览界面与课程发布的课程详情界面一致。 项目采用模板引擎技术实现课程预览界面。什么是模板引擎? 早期我们采用的…...
刷题训练之模拟
> 作者:დ旧言~ > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:熟练掌握模拟算法。 > 毒鸡汤:学习,学习,再学习 ! 学,然后知不足。 > 专栏选自:刷题训…...
视频监控平台:交通运输标准JTT808设备SDK接入源代码函数分享
目录 一、JT/T 808标准简介 (一)概述 (二)协议特点 1、通信方式 2、鉴权机制 3、消息分类 (三)协议主要内容 1、位置信息 2、报警信息 3、车辆控制 4、数据转发 二、代码和解释 (一…...
【C++】多态 — 多态的细节补充(下篇)
前言: 我们学习了多态的形式和如何使用多态,这一章我们将来讲一讲多态的原理… 目录 动态绑定与静态绑定: 动态绑定与静态绑定: 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如…...
系统安全与应用【2】
1.开关机安全控制 1.1 GRUB限制 限制更改GRUB引导参数 通常情况下在系统开机进入GRUB菜单时,按e键可以查看并修改GRUB引导参数,这对服务器是一个极大的威胁。可以为GRUB 菜单设置一个密码,只有提供正确的密码才被允许修改引导参数。 实例&…...
EtherCAT总线速度轴控制功能块(COSESYS ST源代码)
测试环境为汇川PLC,型号 AM402-CPU1608TP、伺服驱动器为禾川X3E,具体通信配置可以参考下面文章链接: 1、使能和点动控制 汇川AM400PLC通过EtherCAT总线控制禾川X3E伺服使能和点动控制-CSDN博客文章浏览阅读31次。进行通信之前需要安装禾川X3E的XML文件,具体方法如下:1、汇…...
【码银送书第十九期】《图算法:行业应用与实践》
作者:嬴图团队 01 前言 在当今工业领域,图思维方式与图数据技术的应用日益广泛,成为图数据探索、挖掘与应用的坚实基础。本文旨在分享嬴图团队在算法实践应用中的宝贵经验与深刻思考,不仅促进业界爱好者之间的交流,…...
无监督式学习
1.是什么? 无监督式学习与监督式学习**最大的区别就是:**没有事先给定的训练实例,它是自动对输入的示例进行分类或者分群; 优点:不需要标签数据,极大程度上扩大了我们的数据样本,其次不受监督信…...
docker 安装镜像及使用命令
目录 1. Mysql2. Redis3. Nginx4. Elasticsearch官网指导 docker pull 容器名:版本号 拉取容器, 不指定版本号默认最新的 run 运行 -d 后台运行 -p 3306:3306 -p是port 对外端口:对内端口 –name xyy_mysql 容器名称 -e MYSQL_ROOT_PASSWORD123456 环境变量 -v 系统地址:docker…...
Python运维之多进程!!
本节的快速导航目录如下喔!!! 一、创建进程的类Process 二、进程并发控制之Semaphore 三、进程同步之Lock 四、进程同步之Event 五、进程优先队列Queue 六、多进程之进程池Pool 七、多进程之数据交换Pipe 一、创建进程的类Process mu…...
Redis(无中心化集群搭建)
文章目录 1.无中心化集群1.基本介绍2.集群说明 2.基本环境搭建1.部署规划(6台服务器)2.首先删除上次的rdb和aof文件(对之前的三台服务器都操作)1.首先分别登录命令行,关闭redis2.清除/root/下的rdb和aof文件3.把上次的…...
基于springboot+jsp+Mysql的商务安全邮箱邮件收发
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
三.Django--ORM(操作数据库)
目录 1 什么是ORM 1.1 ORM优势 1.2ORM 劣势 1.3 ORM与数据库的关系 2 ORM 2.1 作用 2.2 连接数据库 2.3 表操作--设置字段 2.4 数据库的迁移 写路由增删改查操作 项目里的urls.py: app里的views.py: 注意点: 1 什么是ORM ORM中文---对象-关系映射 在MTV,MVC设计…...
【华为】AC直连二层组网隧道转发实验配置
【华为】AC直连二层组网隧道转发实验配置 实验需求拓扑配置AC数据规划表 AC的配置顺序AC1基本配置(二层通信)AP上线VAP组关联--WLAN业务流量 LSW1AR1STA获取AP的业务流量 配置文档 实验需求 AC组网方式:直连二层组网。 业务数据转发方式:隧道转发。 DHC…...
第 129 场 LeetCode 双周赛题解
A 构造相同颜色的正方形 枚举:枚举每个 3 3 3\times 3 33的矩阵,判断是否满足条件 class Solution {public:bool canMakeSquare(vector<vector<char>>& grid) {for (int i 0; i < 2; i)for (int j 0; j < 2; j) {int c1 0, c…...
GStreamer日志调试笔记
1、查询所有分类 #gst-launch-1.0 --gst-debug-help 2、查询videotestsrc的日志 #gst-launch-1.0 --gst-debug-help | findstr videotestsrc 结果: 3、使用--gst-debug设置相应日志类型的相应等级,越大显示日志越多,排查内存泄露可以设置为9 …...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
