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

:

 

最新文章

  1. SILVERLIGHT 应急卫生模拟演练项目之childwindow
  2. springMVC之servlet-config.xml配置
  3. 【Java】XML解析之SAX
  4. Java-基本的程序设计结构
  5. cocos2dx-3.x 导出自定义类到 lua 过程详解
  6. EdgesForExtendedLayout
  7. Rman-03002,Rman-12010,Rman-12012
  8. 6.ipv6地址配置
  9. linux源代码阅读笔记 高速缓冲区管理
  10. RasAPI函数实现PPPOE拨号
  11. vc关于文件拷贝
  12. 给你的Cordova HybridApp加入Splash启动页面
  13. PreferenceActivity详解
  14. Leetcode_96_Unique Binary Search Trees
  15. Ubuntu 16.04 安装 Docker
  16. oo第二次总结
  17. php 常用字符函数学习
  18. box-flex 弹性合布局+WebApp布局自适应
  19. 像素 转换 px dp
  20. WebKit的Platform接口部分

热门文章

  1. CentOS 6\7修改主机名
  2. 吴裕雄 python 机器学习——支持向量机非线性回归SVR模型
  3. 错误:javax.servlet.http.HttpServlet&quot; was not found on the Java Build Path
  4. git常用命令(二)
  5. innerHTML动态添加标签的注意事项
  6. 中小学信息学奥林匹克竞赛-理论知识考点--ASCII
  7. Mybatis学习的一些细节
  8. Vscode插件--微信小程序格式化以及高亮组件wxml-vscode
  9. 基于mybatis设计简单OA系统问题3
  10. JQuery实现父级选择器(广告实现)