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 ...
随机推荐
- Last_SQL_Errno: 1050
主库上create table,从库上存在. 报错信息如下所示: Last_SQL_Errno: 1050 Last_SQL_Error: ...
- hash函数的选择
哈稀函数按照定义可以实现一个伪随机数生成器(PRNG),从这个角度可以得到一个公认的结论:哈希函数之间性能的比较可以通过比较其在伪随机生成方面的比较来衡量. 一般来说,对任意一类的数据存在一个理论上完 ...
- php无法保存SESSION问题总汇
昨天客户又过来说网站的问题,说的也都是些毛毛雨的东西,管理那么多网站,再有这么些客户的存在,本人也是累了,但当登录后台的时候突然发现后台登录不了,查看了一下验证码服务器端的session为空值,之前登 ...
- scrapy install
csf@ubuntu:~$ sudo apt install python-scrapy
- ES6初识-解构赋值
数组解构赋值 [a,b]=[1,2]; . 方法返回 function f(){ return [1,2] } let a,b; [a,b]=f();//a=1,b=2 function f1() ...
- Inventory Update-freecodecamp算法题目
Inventory Update 1.要求 依照一个存着新进货物的二维数组,更新存着现有库存(在 arr1 中)的二维数组. 如果货物已存在则更新数量 . 如果没有对应货物则把其加入到数组中,更新最新 ...
- Linux文件服务器实战(匿名用户)
一.进程与线程 二.vsftp服务器 1.文件传输协议(file transfer protocol,FTP) 基于该协议ftp客户端和服务端实现文件共享,上传下载文件 FTP基于TCP协议生成一个虚 ...
- Docker自学纪实(五) 使用Dockerfile构建php网站环境镜像
一般呢,docker构建镜像容器的方式有两种:一种是pull dockerhub仓库里面的镜像,一种是使用Dockerfile自定义构建镜像. 很多时候,公司要求的镜像并不一定符合dockerhub仓 ...
- 有一段<script>代码,效果是点击<p>就会弹出信息,但是有的<p>点击会有效果,有的没有效果
问题:有一段<script>代码,效果是点击<p>就会弹出信息,但是有的<p>点击会有效果,有的没有效果 解决: 页面代码是至上而下执行的,如果你的这个标签在< ...
- axios进行ajax请求得不到数据,cookie无法携带问题
这个坑也是很早之前踩过,今天做项目的时候居然忘了,怎么都拿不到数据,果然好记性不如烂笔头,决定写篇博客来祭奠下我的猪脑子: 原因可能就是你发送请求的时候,需要设置cookie,然而你的cookie并没 ...