FTP2
FTP:
环境:windows, python 3.5
功能:
1.用户加密认证,可自行配置家目录磁盘大小
2.多用户登陆
3.查看当前目录(家目录权限下)
4.切换目录(家目录权限下)
5.上传下载,进度条展示,MD5认证
6.下载支持断点续传(仅限传输过程中意外终断,后续支持继续传输)
结构:
ftp_client ---|
bin ---|
start_client.py ......启动客户端
conf---|
config.py ......客户端参数配置
system.ini ......客户端参数配置文件
core---|
ftp_client.py ......客户端主程序
ftp_server ---|
bin ---|
start_server.py ......启动服务端
conf---|
config.py ......服务端参数配置
system.ini ......服务端参数配置文件
core---|
ftp_server.py ......服务端主程序
cmd.py ......执行系统命令,如(mkdir,dir等)
db ---|
data.py ......存取用户数据
home ......用户家目录,每个用户生成各自姓名的家目录
功能实现:
ftp_server.py启动后进入ftp_server.py,执行类MyServer中的handle()方法等待客户端连接;
客户端通过 start_client.py 启动进入 ftp_client.py;
执行 类FtpClient 中的connect()方法建立连接;
进入 interactive()入口,首先调用authenticate()加密登陆,如未注册则调用register()注册;
然和根据输入内容,分别映射进入到相应方法中,再各个方法里;
在各个方法中,首先将action发送给服务端;
服务端根据action将映射到服务端的各个方法中,进行数据交互;
如何使用:
启动start_server.py,启动start_client.py;
首先输入用户名,如用户名未注册则提示注册,注册时需输入家目录磁盘大小,单位为 b,登陆完成,进入菜单;
根据提示输入需要进行的操作:
dir (path):展示当前目录或者展示所给路径目录,如路径超过权限则提示权限不够;
cd (path):切换目录或返回家目录,根据输入的路径切换到相应目录,格式为cd ./path,必须输入./+路径,./表示当前位置,直接cd则返回家目录
put filepath:上传文件到家目录下
get filepath:下载文件到指定目录,只允许下载家目录里存在的文件(下载前请先上传文件到家目录下),支持断点续传
ftp_client:
bin:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import os
import sys
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
import core core.ftp_client.run()
start_client.py
conf:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# _author_=zh
import os
import configparser
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class Configuration(object):
def __init__(self):
self.config = configparser.ConfigParser()
self.name = PATH+os.sep+"conf"+os.sep+"system.ini" def init_config(self):
# 初始化配置文件,ip :客户端IP,port:客户端端口
if not os.path.exists(self.name):
self.config["config"] = {"ip": "localhost", "port": 1234}
self.config.write(open(self.name, "w", encoding="utf-8", )) def get_config(self, head="config"):
'''
获取配置文件数据
:param head: 配置文件的section,默认取初始化文件config的数据
:return:返回head中的所有数据(列表)
'''
self.init_config() # 取文件数据之前生成配置文件
self.config.read(self.name, encoding="utf-8")
if self.config.has_section(head):
section = self.config.sections()
return self.config.items(section[0])
config.py
core:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import socket
import os
import sys
import json
import hashlib
from conf import config
# 服务端交互传送结果
SIGN_DICT = {
'': '用户名存在',
'': '用户名不存在',
'': '路径不存在',
'': '文件超过磁盘限额',
'': '传输完毕',
'': '传输成功',
'': '传输失败',
'': '文件无权限访问',
'': '文件不存在'
} class FtpClient(object):
def __init__(self):
self.client = socket.socket() def connect(self, ip, port):
self.client.connect((ip, port)) def interactive(self):
# 入口
global catalog
name = self.authenticate()
self.catalog = name
while True:
cmd = input("%s>>" % self.catalog).strip()
if len(cmd) == 0:
continue
cmd_str = cmd.split()[0]
if hasattr(self, cmd_str):
func = getattr(self, cmd_str)
func(cmd, name)
else:
print("输入格式错误")
self.help() @staticmethod
def help():
msg = '''
dir path(无path默认显示当前目录下文件)
cd path(切换目录)
get filename(下载文件)
put filename(上传文件)
quit 退出
'''
print(msg) def register(self, name):
# 注册
name = name
pwd = input("请输入密码(q退出):").strip()
limit_size = input("请输入用户磁盘空间(单位:b):")
if pwd.lower() == 'q':
exit()
hash_md5 = hashlib.md5()
hash_md5.update(pwd.encode())
pwd_md5 = hash_md5.hexdigest()
user_data = {
"name": name,
"pwd": pwd_md5,
"limit_size": limit_size
}
self.client.send(json.dumps(user_data).encode()) def authenticate(self):
# 登陆
while True:
name = input("请输入用户名:").strip().lower()
msg_dict = {
"action": "authenticate",
"username": name
}
self.client.send(json.dumps(msg_dict).encode())
server_response = self.client.recv(1024)
server_response = json.loads(server_response.decode())
if server_response["sign"] == '':
print(SIGN_DICT[''])
print("请注册!")
self.register(name)
return name
elif server_response["sign"] == '':
value = server_response["value"]
while True:
pwd = input("请输入密码:").strip()
hash_md5 = hashlib.md5()
hash_md5.update(pwd.encode())
pwd_md5 = hash_md5.hexdigest()
if value["pwd"] == pwd_md5:
print("登陆成功")
return name
else:
print("密码错误,请重新输入") def dir(self, *args):
# 查看当前目录
cmd_split = args[0]
msg_dict = {
"action": "dir",
"command": cmd_split,
"user_name": self.catalog
}
self.client.send(json.dumps(msg_dict).encode())
server_response = self.client.recv(1024)
answer = json.loads(server_response.decode())
if type(answer) is list:
print(answer[0], answer[1])
else:
print(answer) def cd(self, *args):
# 切换目录,不带路径则直接切换回家目录下
cmd_split = args[0].split()
if len(cmd_split) > 1:
dir_path = cmd_split[1]
msg_dict = {
"action": "cd",
"dir_path": dir_path,
"now_path": self.catalog
}
self.client.send(json.dumps(msg_dict).encode())
data = self.client.recv(1024)
if data.decode() == '':
self.catalog = dir_path.replace('.', self.catalog)
print("切换成功")
else:
print(SIGN_DICT[data.decode()])
else:
self.catalog = args[1] def put(self, *args):
# 上传
cmd_split = args[0].split()
if len(cmd_split) > 1:
filename = cmd_split[1]
if os.path.isfile(filename):
file_size = os.stat(filename).st_size
msg_dict = {
"action": "put",
"filename": filename,
"size": file_size,
"user_name": args[1]
}
self.client.send(json.dumps(msg_dict).encode())
# 防止黏包,等服务器确认
server_response = (self.client.recv(1024)).decode()
if server_response == '':
file = open(filename, 'rb')
hash_md5 = hashlib.md5()
send_data = 0
for line in file:
self.client.send(line)
send_data += len(line)
self.progress_bar(send_data, file_size)
hash_md5.update(line)
else:
file.close()
pwd_md5 = hash_md5.hexdigest()
server_answer = (self.client.recv(1024)).decode()
if server_answer == '':
self.client.send(pwd_md5.encode())
put_answer = (self.client.recv(1024)).decode()
print(SIGN_DICT[put_answer])
else:
print(SIGN_DICT[server_response])
else:
print(filename, "is not exist")
else:
print("请输入上传文件路径") def get(self, *args):
# 下载,支持断点续传
cmd_split = args[0].split()
if len(cmd_split) > 1:
file_path = cmd_split[1]
msg_dict = {
"action": "get",
"file_path": file_path,
"user_name": args[1]
}
self.client.send(json.dumps(msg_dict).encode())
server_response = (self.client.recv(1024)).decode()
if server_response == '':
self.client.send(''.encode()) # 避免黏包
file_data = self.client.recv(1024)
file_length = (json.loads(file_data.decode()))["length"]
file_name = (json.loads(file_data.decode()))["file_name"]
get_path = input("请输入存放路径:")
if os.path.isdir(get_path):
if os.path.exists(get_path+os.sep+file_name):
choose = input("文件已存在,请选择(1.覆盖,2.重命名,3续传):")
if choose == "":
os.remove(get_path+os.sep+file_name)
exists_length = 0
get_path = get_path+os.sep+file_name
elif choose == '':
exists_length = 0
get_path = get_path + os.sep + file_name+".new"
elif choose == "":
exists_length = os.stat(get_path+os.sep+file_name).st_size
get_path = get_path + os.sep + file_name
else:
exists_length = 0
get_path = get_path + os.sep + file_name
self.client.send(str(exists_length).encode())
file = open(get_path, "wb")
file.seek(int(exists_length))
hash_md5 = hashlib.md5()
get_length = exists_length
while get_length < int(file_length):
data = self.client.recv(1024)
file.write(data)
get_length += len(data)
hash_md5.update(data)
self.progress_bar(get_length, int(file_length))
else:
pwd_md5 = hash_md5.hexdigest()
file.close()
self.client.send('传输完成'.encode())
get_md5 = (self.client.recv(1024)).decode()
if get_md5 == pwd_md5:
print("下载成功")
else:
print("下载失败")
os.remove(get_path)
else:
print("路径输入错误")
else:
print(SIGN_DICT[server_response]) else:
print("请输入下载文件路径") @staticmethod
def quit(*args):
# 退出
print("%s已退出" % args[1])
exit() @staticmethod
def progress_bar(send_data, all_data):
# 进度条展示
# send_data:已发送的长度,all_data:总长度
ret = send_data / all_data
num = int(ret * 100)
view = '\r [%-100s]%d%%' % ("=" * num, 100,)
sys.stdout.write(view)
sys.stdout.flush() def run():
client = FtpClient()
conf = config.Configuration()
conf_data = conf.get_config()
client.connect(conf_data[0][1], int(conf_data[1][1]))
client.help()
client.interactive()
ftp_client.py
ftp_server:
bin
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import os
import sys
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
import core core.ftp_server.run()
start_server.py
conf:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# _author_=zh
import os
import configparser
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class Configuration(object):
def __init__(self):
self.config = configparser.ConfigParser()
self.name = PATH+os.sep+"conf"+os.sep+"system.ini" def init_config(self):
# 初始化配置文件,IP:服务端IP,port:服务端端口,limit_size:家目录容量,HOME_PATH:用户家目录地址
if not os.path.exists(self.name):
self.config["config"] = {"ip": "localhost", "port": 1234}
self.config.write(open(self.name, "w", encoding="utf-8", )) def get_config(self, head="config"):
'''
获取配置文件数据
:param head: 配置文件的section,默认取初始化文件config的数据
:return:返回head中的所有数据(列表)
'''
self.init_config() # 取文件数据之前生成配置文件
self.config.read(self.name, encoding="utf-8")
if self.config.has_section(head):
section = self.config.sections()
return self.config.items(section[0])
config.py
core:
#!/usr/bin/env python
# _*_coding:utf-8_*_
# _author_=zh
import os
import locale
import codecs
import subprocess
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"home" def order(cmd):
'''
执行命令结果输出到屏幕
:param cmd: 输入的命令
:return:
'''
word = subprocess.Popen(args=cmd, cwd=PATH, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
# 将结果decode输出,自动获取不同操作系统的默认编码
return word.stderr.read().decode(codecs.lookup(locale.getpreferredencoding()).name), \
word.stdout.read().decode(codecs.lookup(locale.getpreferredencoding()).name)
cmd.py
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import socketserver
import json
import hashlib
import os
from conf import config
import db
import core PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class MyServer(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
data = self.request.recv(1024)
if len(data) > 0:
cmd_dict = json.loads(data.decode())
action = cmd_dict['action']
if hasattr(self, action):
func = getattr(self, action)
func(cmd_dict)
else:
break
except (ConnectionResetError, OSError) as e:
print(e)
break def dir(self, *args):
# 查看目录
cmd_dict = args[0]
command_data = cmd_dict["command"]
command = command_data.split()
user_name = cmd_dict["user_name"]
home_path = PATH + os.sep + "home" + os.sep + user_name
if len(command) > 1:
path_data = command[1]
if path_data[:len(home_path)] == home_path:
answer = core.cmd.order(command_data)
else:
answer = '路径错误'
else:
answer = core.cmd.order('%s %s' % (command[0],home_path))
self.request.send(json.dumps(answer).encode()) def cd(self, *args):
# 切换目录,默认显示家目录,格式为cd ./path
cmd_dict = args[0]
dir_path = cmd_dict["dir_path"]
now_path = cmd_dict["now_path"]
if dir_path[0] is '.':
dir_path = dir_path.replace('.', PATH+os.sep+"home"+os.sep+now_path)
print("dir_path:", dir_path)
if os.path.exists(dir_path):
sign = ''
else:
sign = ''
else:
sign = ''
self.request.send(sign.encode()) def put(self, *args):
# 上传
cmd_dict = args[0]
filename = cmd_dict["filename"][cmd_dict["filename"].rfind("\\")+1:]
file_size = cmd_dict["size"]
user_name = cmd_dict["user_name"]
value = db.data.read(user_name)
limit_size = int(value["limit_size"])
home_path = PATH+os.sep+"home"+os.sep+user_name
if limit_size > file_size:
if os.path.isfile(home_path+os.sep+filename):
file_path = home_path+os.sep+filename+'.new'
else:
file_path = home_path + os.sep + filename
sign = ""
else:
sign = ""
self.request.send(sign.encode())
if sign == "":
file = open(file_path, 'wb')
recv_size = 0
hash_md5 = hashlib.md5()
while recv_size < file_size:
data = self.request.recv(1024)
hash_md5.update(data)
file.write(data)
recv_size += len(data)
else:
self.request.send("".encode())
file.close()
hash_pwd = (self.request.recv(1024)).decode()
if hash_pwd == hash_md5.hexdigest():
sign = ''
value["limit_size"] = limit_size - file_size
db.data.write(user_name, value)
else:
sign = ''
os.remove(file_path)
self.request.send(sign.encode()) def get(self, *args):
# 下载
cmd_dict = args[0]
file_path = cmd_dict["file_path"]
user_name = cmd_dict["user_name"]
user_path = PATH+os.sep+"home"+os.sep+user_name
if file_path[:len(user_path)] == user_path:
if os.path.isfile(file_path):
sign = ''
else:
sign = ''
else:
sign = ''
self.request.send(sign.encode())
if sign == '':
self.request.recv(1024)
server_msg = {
"length": os.stat(file_path).st_size,
"file_name": file_path[file_path.rfind("\\")+1:]
}
self.request.send(json.dumps(server_msg).encode())
exists_length = (self.request.recv(1024)).decode()
file = open(file_path, "rb")
file.seek(int(exists_length))
hash_md5 = hashlib.md5()
for line in file:
self.request.send(line)
hash_md5.update(line)
else:
file.close()
pwd_md5 = hash_md5.hexdigest()
self.request.recv(1024)
self.request.send(pwd_md5.encode()) def authenticate(self, *args):
# 登陆认证
cmd_dict = args[0]
name = cmd_dict["username"]
value = db.data.read(name) # 用户存在返回用户数据,不存在返回None
if value:
num_sign = ""
else:
num_sign = ""
send_data = {
"sign": num_sign,
"value": value
}
self.request.send(json.dumps(send_data).encode())
# 用户名不存在时注册,需接收注册信息并写入数据库
if not value:
user_data = self.request.recv(1024)
if len(user_data) > 0:
user_data = json.loads(user_data.decode())
db.data.write(user_data["name"], user_data)
os.popen("mkdir %s" % (PATH+os.sep+"home"+os.sep+name)) def run():
conf = config.Configuration()
conf_data = conf.get_config()
servers = socketserver.ThreadingTCPServer((conf_data[0][1], int(conf_data[1][1])), MyServer)
servers.serve_forever()
ftp_server.py
db:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# _author_=zh
import shelve
import os
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"db"+os.sep def write(name, data):
'''
存储个人信息
:param data: 存储的数据
:param name: shelve的key
'''
file = shelve.open("%sdata" % PATH)
file[name] = data
file.close() def read(name):
'''
读取个人信息
:param name:存储时传入的name
:return:返回个人信息
'''
file = shelve.open("%sdata" % PATH)
if name in list(file.keys()):
data = file[name]
else:
data = None
file.close()
return data
data.py
:
FTP2的更多相关文章
- 个人电脑搭建ftp----------------2
个人电脑搭建ftp 从上一次搭建好的局域网继续完成我的后续. 打开windows10 控制面板 点击启用或关闭windows功能 找到Internet Information Services,开启所 ...
- Atitit ftp原理与解决方案
Atitit ftp原理与解决方案 Deodeo sh shmayama ..search ftp.. 1. http和ftp都只是通信协议,就是只管传输那一块的,那为什么不能使用ftp来显示网页?? ...
- deiban8 sourcelist
deb http://ftp2.cn.debian.org/debian/ jessie main non-free contribdeb http://ftp2.cn.debian.org/debi ...
- [原]centos6.5系统可用yum源(32位)以及rpmforge
[10gen] name=10gen Repository baseurl=http://downloads-distro.mongodb.org/repo/redhat/os/i686 gpgche ...
- linux压缩解压文件
首先进入文件夹 cd /home/ftp2/1520/web 压缩方法一:压缩web下的888.com网站 zip -r 888.com.zip888.com 压缩方法二:将当前目录下的所有文件和文件 ...
- vsftp linux
查看是否安装 rpm -qa|grep vsftpdyum -y install vsftpd /etc/vsftpd/vsftpd.conf #主配置文件 /usr/sbin/vsftpd ...
- VSFTP服务器
vsftpd 是"very secure FTP daemon"的缩写,安全性是它的一个最大的特点.vsftpd 是一个 UNIX 类操作系统上运行的服务器的名字,它可以运行在诸如 ...
- VSFTP安全加固
这几天在公司需要做基线安全,一直都没有经验,所以在网上找了一些,做来参考学习. vsftp配置详解 这里是对vsftp配置文件的详细解释,主要参考了<RedHat8.0网络服务>一书中&l ...
- 在Linux中搭建一个FTP服务器
在Linux中搭建一个ftp服务器,以供两个工作小组保管文件使用.禁用匿名.第一个小组使用ftp账号:ftp1,工作目录在:/var/ftp/ftp1:第二个小组使用ftp2,工作目录在:/var/f ...
随机推荐
- 【BZOJ3506】[CQOI2014] 排序机械臂(Splay)
点此看题面 大致题意: 给你\(n\)个数.第一次找到最小值所在位置\(P_1\),翻转\([1,P_1]\),第二次找到剩余数中最小值所在位置\(P_2\),翻转\([2,P_2]\),以此类推.求 ...
- vuejs父子组件的数据传递
在vue中,父组件往子组件传递参数都是通过属性的形式来传递的 <div id='root'> <counter :count = '1'></counter> &l ...
- Laravel5 构造器高级查询条件写法
<?php #DB 高级查询 // select * from table where A and B or C $all_data = DB::table("shopnc_goods ...
- Spring Boot的Maven插件Spring Boot Maven plugin详解
Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Mave ...
- 2.1-Java语言基础(keyword)
2.1 keyword watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbXNpcmVuZQ==/font/5a6L5L2T/fontsize/400/fi ...
- C# is运算符
一.C# is运算符 is运算符用于检查对象是否与给定类型兼容(兼容表示对象是该类型,或者派生于该类型). is运算符用来检查对象(变量)是否属于某种数据类型,如int.string.bool.dou ...
- Python爬虫,看看我最近博客都写了啥,带你制作高逼格的数据聚合云图
转载请标明出处: http://blog.csdn.net/forezp/article/details/70198541 本文出自方志朋的博客 今天一时兴起,想用python爬爬自己的博客,通过数据 ...
- 在VSCode中使用码云
在VSCode中使用码云 一.SSH公钥 使用SSH公钥可以让你在你的电脑和码云通讯的时候使用安全连接(Git的Remote要使用SSH地址) 链接 https://gitee.com/profile ...
- Aspects– iOS的AOP面向切面编程的库
简介 一个简洁高效的用于使iOS支持AOP面向切面编程的库.它可以帮助你在不改变一个类或类实例的代码的前提下,有效更改类的行为.比iOS传统的 AOP方法,更加简单高效.支持在方法执行的前/后或替代原 ...
- ionic 命令cordova
安装android platform : ionic platform add android 安装一维码cordova插件 :cordova plugin add https://github.co ...