# 55. 网络编程大作业 - 类网盘项目
# 要求
- 登录,注册功能
- 登录,就是让程序检查用户文件,查看有没有这用户
- 注册功能,比较多
- 客户端的密码传输,需要使用用户名进行加盐
- 服务端接到用户端的密码传输后,不能马上写入用户文件中,需要在服务端在加盐,加盐的值,可以写到配置文件中
- 用户在注册中,密码至少达到8位数,其他限制自由
- 用户成功注册后,要在服务端的资源目录中创建用户的用户名目录,作为这个用户的云盘存储目录
- 主体功能
- 输入 .. 就代表要进入上一层目录
- 注意:这里最前的目录为用户初始目录,不能超过这个目录
- 输入 目录名 就代表进入这个目录
- 输入 文件名 就代表下载这个文件
- 用户下载到的文件,让用户选择
- 默认是,下载到程序的资源目录中
- 可以更改下载路径,需要用户输入绝对路径
- 用户下载到的文件,让用户选择
- 输入 up 就代表上传文件
- 输入 mk 就代表在当前目录中,创建一个目录
- 输入 .. 就代表要进入上一层目录
- 服务端架构
- 启动文件,需要使用 IO多路复用架构
- 核心文件,使用面向对象跟函数编写
- 启动文件调用核心文件时,需要使用线程池调用,保证这程序可以多人连接
- socker模块,使用默认的TCP协议
- 可以加上日志,不要求,博主也只加了日志的模板,懒得一一去加日志了
- 客户端架构
- 客户端没什么要求,就核心文件,需要按面向对象编写
# 服务端
# 启动文件 - server.py
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
from concurrent.futures import ThreadPoolExecutor
import select
import socket
import sys
import os
import logging
server_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(server_path)
from conf import conf
from core import core
## 创建一个对象(实例化logging模块中的getLogger()类)
logger = logging.getLogger()
## 创建一个文件管理操作符
fh = logging.FileHandler("../log/logging.log",encoding="utf-8")
## 创建一个屏幕管理操作符
sh = logging.StreamHandler()
## 创建一个日志输出的格式
journal1 = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
## 文件管理操作符 绑定 日志格式
fh.setFormatter(journal1)
## 屏幕管理操作符 绑定 日志格式
sh.setFormatter(journal1)
## 设置日志输出等级
logger.setLevel(logging.DEBUG)
## 创建好的对象 绑定 文件管理操作符
logger.addHandler(fh)
## 创建好的对象 绑定 屏幕管理操作符
logger.addHandler(sh)
sk = socket.socket()
sk.bind((conf.ip,conf.port))
sk.listen()
server_del = [] ## 要删除的列表
server_r = [sk] ## 监听的列表
while 1:
r,w,x = select.select(server_r,[],[])
if r:
for i in r:
if i == sk:
conn,addr = i.accept()
server_r.append(conn)
print(server_r)
else:
print(i)
te = ThreadPoolExecutor(20)
te.submit(core.server_user,i,logger,conf.mi)
server_del.append(i)
if server_del:
for s in server_del:
server_r.remove(s)
server_del.clear()
# 核心文件 - core.py
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
import struct
import json
import hashlib
import os
if __name__ == '__main__':
import socket
sk = socket.socket()
sk.bind(("127.0.0.1",18080))
sk.listen()
class tool():
def __init__(self):
pass
def json(self,num,ty="dumps"):
if ty == "dumps":
user_json = json.dumps(num)
user_b = struct.pack("i", len(user_json))
return user_json, user_b
def struct(self,con):
server_struct_size = struct.unpack("i", con.recv(4))[0]
server_struct_user = json.loads(con.recv(server_struct_size).decode("utf-8"))
return server_struct_user
class user():
def __init__(self,con,mi,to):
self.con = con
self.mi = mi
self.to = to
def Registration(self):
self.con.send("1".encode("utf-8"))
while 1:
user, paawd = self.to.struct(self.con)
Registration_user_vs = 1
with open("../conf/user") as f:
for i in f:
f_user, f_pawwd = i.strip().split("|")
if f_user == user:
self.con.send("2".encode("utf-8"))
Registration_user_vs = 0
if Registration_user_vs == 0:
continue
md5 = hashlib.md5(self.mi.encode("utf-8"))
md5.update(paawd.encode("utf-8"))
md5_paawd = md5.hexdigest()
with open("../conf/user", "a") as f:
f.write("%s|%s" % (user, md5_paawd))
f.write("\n")
self.con.send("1".encode("utf-8"))
os.mkdir("../data/%s" % user)
break
def register(self):
while 1:
self.con.send("1".encode("utf-8"))
user, paawd = self.to.struct(self.con)
md5 = hashlib.md5(self.mi.encode("utf-8"))
md5.update(paawd.encode("utf-8"))
paawd = md5.hexdigest()
with open("../conf/user") as f:
for i in f:
f_user, f_pawwd = i.strip().split("|")
if user == f_user:
if paawd == f_pawwd:
self.con.send("1".encode("utf-8"))
return 1
else:
self.con.send("2".encode("utf-8"))
break
class operation():
def __init__(self,con,to):
self.con = con
self.to = to
def Capacity(self):
while 1:
capacity_user = self.to.struct(self.con)
user_list = []
if len(capacity_user) == "1":
capacity_user = capacity_user[0]
else:
capacity_user = "/".join(capacity_user)
for i in os.listdir("../data/%s" % capacity_user):
if os.path.isdir("../data/%s/%s" % (capacity_user, i)):
user_list.append("\033[32m%s\033[0m" % i)
else:
user_list.append(i)
user_list.append(capacity_user)
cc_json, cc_b = self.to.json(user_list)
self.con.send(cc_b + cc_json.encode("utf-8"))
behavior = self.to.struct(self.con)
if behavior == "1":
continue
elif behavior == "upload":
self.upload(user_list)
elif behavior == "mkdir":
self.mkdir(capacity_user)
elif behavior == "download":
self.download(capacity_user)
def upload(self,user_list):
self.con.send("1".encode("utf-8"))
up_user, up_size, up_md5 = self.to.struct(self.con)
up_directory = user_list.pop()
up_u = 0
for i in user_list:
if up_user == i:
self.con.send("9".encode("utf-8"))
break
elif up_user == "\033[32m%s\033[0m" % i:
self.con.send("9".encode("utf-8"))
break
else:
self.con.send("8".encode("utf-8"))
up_u = 1
if up_u:
up_directory = "../data/%s/%s" % (up_directory, up_user)
with open(up_directory, "ab") as f:
while up_size:
content = self.con.recv(1024)
f.write(content)
up_size -= len(content)
up_md5_1 = hashlib.md5()
with open(up_directory, "rb") as f:
for i in f:
up_md5_1.update(i)
up_md5_1 = up_md5_1.hexdigest()
if up_md5 == up_md5_1:
self.con.send("4".encode("utf-8"))
else:
self.con.send("5".encode("utf-8"))
os.remove("up_directory")
def mkdir(self,mk_initial):
self.con.send("1".encode("utf-8"))
mk_file = self.to.struct(self.con)
if not os.path.exists("../data/%s/%s"%(mk_initial,mk_file)):
os.mkdir("../data/%s/%s"%(mk_initial,mk_file))
if os.path.isdir("../data/%s/%s"%(mk_initial,mk_file)):
self.con.send("2".encode("utf-8"))
else:
self.con.send("9".encode("utf-8"))
if os.path.exists("../data/%s/%s"%(mk_initial,mk_file)):
os.remove("../data/%s/%s"%(mk_initial,mk_file))
else:
self.con.send("8".encode("utf-8"))
def download(self,capacity_user):
self.con.send("1".encode("utf-8"))
do_1 = self.con.recv(1).decode("utf-8")
if do_1 == "2":
do_file = self.to.struct(self.con)
do_file_list = []
do_file_size = os.path.getsize("../data/%s/%s"%(capacity_user,do_file))
up_md5 = hashlib.md5()
with open(os.path.abspath("../data/%s/%s"%(capacity_user,do_file)), "rb") as f:
for i in f:
up_md5.update(i)
up_md5 = up_md5.hexdigest()
do_file_list.extend([do_file_size,up_md5])
do_json,do_b = self.to.json(do_file_list)
self.con.send(do_b + do_json.encode("utf-8"))
with open(os.path.abspath("../data/%s/%s"%(capacity_user,do_file)), "rb") as f:
while do_file_size:
content = f.read(1024)
self.con.send(content)
do_file_size -= len(content)
def server_user(con,logge,mi):
while 1:
server_user_size = struct.unpack("i",con.recv(4))[0]
server_user = json.loads(con.recv(server_user_size).decode('utf-8'))
to = tool()
u = user(con,mi,to)
o = operation(con,to)
if server_user[1] == "register":
# rg = register(con,mi)
u.register()
o.Capacity()
elif server_user[1] == "Registration":
# Registration(con,mi)
u.Registration()
if __name__ == '__main__':
conn,addr = sk.accept()
server_user(conn,1,"网盘")
# 配置文件
# 主配置文件
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
## 服务器IP
ip = "127.0.0.1"
## 服务器端口
port = 18080
## 服务端对密码进行第二次加盐
mi = "网盘"
# 用户文件
jc|7e990c6f24d73f5fb556c31b1107634f
# 客户端
# 启动文件 - client.py
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
import socket
import sys
import os
server_path = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
sys.path.append(server_path)
from core import core
while 1:
print("=" * 30)
print("请选择您要进行的操作:")
rs = {1:"登录",2:"注册",3:"退出"}
print("="*30)
[print("%s:%s"%(k,v)) for k,v in rs.items()]
user_input = input(">>>:")
if user_input.isalpha():
continue
elif user_input == "1":
register = core.user("register")
rst1,rst_user = register.register()
if rst1 == "1":
print("=" * 30)
print("默认进入并打开用户的当前空间")
register.Capacity(rst_user)
elif user_input == "2":
Registration = core.user("Registration")
Registration.Registration()
elif user_input == "3":
print("=" * 30)
print("程序退出")
exit()
else:
continue
# 核心文件 - core.py
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
import socket
import sys
import os
import json
import struct
import hashlib
server_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(server_path)
from conf import conf
sk = socket.socket()
sk.connect((conf.ip,conf.port))
class user:
def __init__(self,user):
self.user = user
self.user_list = ["user"]
def json(self,num,ty="dumps"):
if ty == "dumps":
user_json = json.dumps(num)
user_b = struct.pack("i", len(user_json))
return user_json,user_b
def struct(self):
struct_size = struct.unpack("i", sk.recv(4))[0]
struct_user = json.loads(sk.recv(struct_size).decode("utf-8"))
return struct_user
def hashlib(self,user,pawwd):
md5 = hashlib.md5(user.encode("utf-8"))
md5.update(pawwd.encode("utf-8"))
pawwd = md5.hexdigest()
return pawwd
def run(self):
self.user_list.append(self.user)
user_json,user_b = self.json(self.user_list)
sk.send(user_b + user_json.encode("utf-8"))
def register(self):
self.run()
rs = sk.recv(1).decode("utf-8")
while 1:
if rs:
rs_user_list = []
rs_user = input("用户名:")
rs_pass = input("密码:")
rs_pass = self.hashlib(rs_user,rs_pass)
rs_user_list.extend([rs_user, rs_pass])
rs_json,rs_b = self.json(rs_user_list)
sk.send(rs_b + rs_json.encode("utf-8"))
rs1 = sk.recv(1).decode("utf-8")
if rs1 == "1":
print("=" * 30)
print("登录成功")
return rs1,rs_user
elif rs1 == "2":
print("=" * 30)
print("用户名或密码错误")
continue
else:
print("=" * 30)
print("登录失败")
continue
else:
print("=" * 30)
print("远程服务器,无响应")
break
def Registration(self):
self.run()
rt = sk.recv(1).decode("utf-8")
while 1:
if rt:
rt_user_list = []
rt_user = input("用户名:")
rt_pass = input("密码:")
rt_pass1 = input("确定密码:")
if not rt_pass == rt_pass1:
print("=" * 20)
print("二次输入的密码不相同,请重新注册")
continue
elif len(rt_pass) < 8:
print("="*20)
print("密码长度最低为8位数")
continue
rt_pass = self.hashlib(rt_user,rt_pass)
rt_user_list.extend([rt_user,rt_pass])
rt_json, rt_b = self.json(rt_user_list)
sk.send(rt_b + rt_json.encode("utf-8"))
rt_i = sk.recv(1).decode("utf-8")
if rt_i == "1":
print("=" * 30)
print("注册成功")
break
elif rt_i == "2":
print("=" * 30)
print("用户名重复,请重新注册")
continue
else:
print("=" * 30)
print("注册出错")
break
else:
print("=" * 30)
print("远程服务器,无响应")
break
def upload(self):
rt = sk.recv(1).decode("utf-8")
if rt:
if rt == "1":
print("=" * 85)
up = input("请输入你要上传文件的绝对路径:")
print("=" * 85)
up_user = input("是否要使用旧文件名为上传文件名,如果是请直接回车,如果不是请输入新文件名:")
if os.path.exists(os.path.abspath(up)):
if os.path.isfile(up):
up_list = []
if not up_user:
up_path_file = os.path.basename(up)
else:
up_path_file = up_user
up_path_size = os.path.getsize(up)
up_md5 = hashlib.md5()
with open(os.path.abspath(up),"rb") as f:
for i in f:
up_md5.update(i)
up_md5 = up_md5.hexdigest()
up_list.extend([up_path_file,up_path_size,up_md5])
up_json,up_b = self.json(up_list)
sk.send(up_b + up_json.encode("utf-8"))
rt_2 = sk.recv(1).decode("utf-8")
if rt_2:
if rt_2 == "8":
if up_path_size > 1024:
rt_plan_1 = 100 / round(up_path_size / 1024)
rt_plan_2 = 1
else:
rt_plan_2 = 0
with open(os.path.abspath(up),"rb") as f:
print("=" * 85)
while up_path_size:
content = f.read(1024)
sk.send(content)
if rt_plan_2:
print('\r' + rt_plan_2 * '=' + '>' + str(rt_plan_2 * rt_plan_1) + '%', end='')
rt_plan_2 += 1
else:
print('\r' + 1 * '=' + '>' + "100" + '%', end='')
up_path_size -= len(content)
print('')
rt_3 = sk.recv(1).decode("utf-8")
if rt_3:
if rt_3 == "4":
print("=" * 30)
print("上传文件成功")
elif rt_3 == "5":
print("=" * 30)
print("上传文件异常,请重新上传")
else:
print("=" * 30)
print("服务器出现异常错误")
else:
print("=" * 30)
print("服务器无响应")
elif rt_2 == "9":
print("=" * 30)
print("文件名重复")
else:
print("=" * 30)
print("服务器出现异常错误")
else:
print("=" * 30)
print("服务器无响应")
else:
print("=" * 85)
print("要上传的是目录,不是文件,本程序只支持上传文件,请您谅解")
else:
print("=" * 30)
print("输入的文件路径有误")
else:
print("=" * 30)
print("服务器无响应")
def mkdir(self):
rt = sk.recv(1).decode("utf-8")
if rt:
if rt == "1":
print("=" * 85)
mk_inpu = input("请输入目录名:")
mk_json,mk_b = self.json(mk_inpu)
sk.send(mk_b + mk_json.encode("utf-8"))
rt_2 = sk.recv(1).decode("utf-8")
if rt_2:
if rt_2 == "2":
print("=" * 85)
print("创建目录成功")
elif rt_2 == "8":
print("=" * 85)
print("创建的目录名重复")
elif rt_2 == "9":
print("=" * 85)
print("系统错误,创建目录失败")
else:
print("=" * 85)
print("服务器无响应")
else:
print("=" * 85)
print("服务器无响应")
def download(self,file):
rt = sk.recv(1).decode("utf-8")
if rt:
if rt == "1":
do_path = input("是否要已默认为程序中的data目录为下载路径,是直接回车,如想下载到别处,请输入下载绝对路径:")
do_file = input("是否要使用旧文件名为下载文件名,是请直接回车,如果不是,请输入新文件名:")
if not do_file:
do_file = file
if not do_path:
do_path = "../data/"
if not os.path.isdir(os.path.abspath(do_path)):
print("输入的路径不是一个目录,请重新下载")
else:
do_path_file = os.path.abspath(os.path.join(do_path,do_file))
if os.path.exists(do_path_file):
print("文件名跟路径中的目录名或文件名相同,请重新下载")
sk.send("9".encode("utf-8"))
else:
sk.send("2".encode("utf-8"))
do_json, do_b = self.json(file)
sk.send(do_b + do_json.encode("utf-8"))
do_size,do_md5 = self.struct()
with open(do_path_file, "ab") as f:
while do_size:
content = sk.recv(1024)
f.write(content)
do_size -= len(content)
od_md5_1 = hashlib.md5()
with open(do_path_file, "rb") as f:
for i in f:
od_md5_1.update(i)
od_md5_1 = od_md5_1.hexdigest()
if do_md5 == od_md5_1:
print("文件下载成功")
else:
print("文件下载失败")
os.remove(do_path_file)
else:
print("=" * 30)
print("服务器无响应")
def Capacity(self,user):
user_list = [user]
while 1:
cc_json,cc_b = self.json(user_list)
sk.send(cc_b + cc_json.encode("utf-8"))
cc_list = self.struct()
print("=" * 85)
print("""
温馨提醒:
1. 黄色为目录
2. 白色为文件
3. 输入"文件名"默认下载
4. 输入"目录名"默认进入该目录
5. 输入".." 二个小数点,默认返回上级目录
6. 上传:请输入"up",上传的路径,默认在选择上传时的目录中
7. 创建目录:请输入"mk",创建的目录,默认在选择创建时的目录中
注意:本程序不支持上传目录跟下载目录,如果需要上传或下载目录,请自行打包后上传
""")
print("=" * 85)
mcd = cc_list.pop()
print("=" * 20 + mcd + "=" * 20 )
if cc_list:
[print(i) for i in cc_list]
else:
print("=" * 30)
print("空")
while 1:
cc_import = input(">>>:")
if cc_import == "up":
cc_json,cc_b = self.json("upload")
sk.send(cc_b + cc_json.encode("utf-8"))
self.upload()
break
elif cc_import == "mk":
cc_json, cc_b = self.json("mkdir")
sk.send(cc_b + cc_json.encode("utf-8"))
self.mkdir()
break
elif cc_import == "..":
if len(user_list) == 1 or len(user_list) == 0:
cc_json, cc_b = self.json("1")
sk.send(cc_b + cc_json.encode("utf-8"))
print("=" * 30)
print("当前已在用户的最前目录")
break
else:
cc_json, cc_b = self.json("1")
sk.send(cc_b + cc_json.encode("utf-8"))
user_list.pop()
break
else:
for i in cc_list:
if cc_import == i:
cc_json, cc_b = self.json("download")
sk.send(cc_b + cc_json.encode("utf-8"))
self.download(cc_import)
user_ok = 1
break
elif i == '\x1b[32m%s\x1b[0m'%cc_import:
cc_json, cc_b = self.json("1")
sk.send(cc_b + cc_json.encode("utf-8"))
user_list.append(cc_import)
user_ok = 1
break
else:
cc_json, cc_b = self.json("1")
sk.send(cc_b + cc_json.encode("utf-8"))
print("=" * 30)
print("输入有误")
user_ok = 0
continue
if user_ok:
break
# 主配置文件
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from __future__ import division
## 连接服务IP
ip = "127.0.0.1"
## 连接服务端口
port = 18080