当前位置: 首页 > news >正文

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()

五、在终端操作示例

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

http://www.lryc.cn/news/344668.html

相关文章:

  • 个人银行账户管理程序(2)
  • 2024.04.19校招 实习 内推 面经
  • Python并发编程 04 进程与线程基础
  • 模板引擎Freemarker
  • 刷题训练之模拟
  • 视频监控平台:交通运输标准JTT808设备SDK接入源代码函数分享
  • 【C++】多态 — 多态的细节补充(下篇)
  • 系统安全与应用【2】
  • EtherCAT总线速度轴控制功能块(COSESYS ST源代码)
  • 【码银送书第十九期】《图算法:行业应用与实践》
  • 无监督式学习
  • docker 安装镜像及使用命令
  • Python运维之多进程!!
  • Redis(无中心化集群搭建)
  • 基于springboot+jsp+Mysql的商务安全邮箱邮件收发
  • 三.Django--ORM(操作数据库)
  • 【华为】AC直连二层组网隧道转发实验配置
  • 第 129 场 LeetCode 双周赛题解
  • GStreamer日志调试笔记
  • 【api接口开通教程】YouTube Data API v3申请流程
  • .net 6.0 框架集成ef实战,步骤详解
  • [C/C++] -- 观察者模式
  • 秋招算法刷题8
  • Docker使用方法
  • HTML学习|网页基本信息、网页基本标签、图像标签、超链接标签、列表标签、表格标签、媒体元素、页面结构分析、iframe内联框架
  • 001 websocket(评论功能demo)(消息推送)
  • 二分查找向下取整导致的死循环69. x 的平方根
  • Kivy 异步任务
  • DEV--C++小游戏(吃星星(0.1))
  • LINUX 入门 4