一. 题目介绍
请基于TCP协议实现一个网盘系统,包含客户端、服务端,各自需求如下:
- 客户端
- 用户注册,注册成功之后,在服务端的指定目录下为此用户创建一个文件夹,该文件夹下以后存储当前用户的数据(类似于网盘)。
- 用户登录
- 查看网盘目录下的所有文件(一级即可),ls命令
- 上传文件,如果网盘已存在则重新上传(覆盖)。
- 下载文件(进度条)
先判断要下载本地路径中是否存在该文件。
- 不存在,直接下载
- 存在,则让用户选择是否续传(继续下载)。
- 续传,在上次的基础上继续下载。
- 不续传,从头开始下载。
服务端
-
支持注册,并为用户初始化相关目录。
注册成功之后,将所有用户信息存储到特定的Excel文件中
-
支持登录
-
支持查看当前用户网盘目录下的所有文件。
-
支持上传
-
支持下载
二.设计思路
1. 服务端代码模块设计思路,可以实现单服务功能和多服务兼容功能。
设计思路代码体现:
class PanHandler(object):
""" 业务功能代码 """
def execute(self):
pass
class Server(object):
""" 基于普通socket的服务端 """
def run(self, handler_class):
# socket服务端
# 一旦接受到请求之后,就让他执行 PanHandler
instance = handler_class()
instance.execute()
pass
class SelectServer(object):
""" 基于IO多路复用的socket的服务端 """
def run(self, handler_class):
# socket服务端(IO多路复用)
# 一旦接受到请求之后,就让他执行 PanHandler
instance = handler_class()
instance.execute()
pass
if __name__ == '__main__':
server = Server()
# server = SelectServer()
server.run(PanHandler)
上述代码缺点就是:基于IO多路复用的socket的服务端的server里面的代码处理逻辑相对于普通的socket的服务端还是比较复杂的,为了兼容二者之间的差异,可以做兼容2种服务的代码优化:
import socket
import select
class PanHandler(object):
""" 业务功能代码 """
def __init__(self, conn):
self.conn = conn
def execute(self):
"""
处理客户端的请求
:return: False,断开连接;True,继续执行当前客户端发来的请求。
"""
data = self.conn.recv(1024)
content = data.decode('utf-8')
if content.upper() == "Q":
return False
# 1.处理登录
# "login wupeiqi xxx"
# 2.注册
# register wupeiqi xxx
# ....
self.conn.sendall(b'xxxx')
return True
class Server(object):
""" 基于普通socket的服务端 """
def run(self, handler_class):
# socket服务端
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("IP", "端口"))
sock.listen(5)
while True:
# 等待,客户端发来连接
conn, addr = sock.accept()
# 新客户端到来。 PanHandler对象
instance = handler_class(conn)
# 处理客户端的请求。 PanHandler对象.execute
while True:
result = instance.execute()
if not result:
break
conn.close()
sock.close()
class SelectServer(object):
""" 基于IO多路复用的socket的服务端 """
def run(self, handler_class):
server_object = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_object.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_object.setblocking(False)
server_object.bind(("IP", "端口"))
server_object.listen(5)
socket_object_list = [server_object, "客户端socket对象1","客户端socket对象2" ]
conn_handler_map = {
"客户端socket对象1": PanHandler(conn1),
"客户端socket对象2": PanHandler(conn2),
}
while True:
# r = ["客户端socket对象4", ]
r, w, e = select.select(socket_object_list, [], [], 0.05)
for sock in r:
# sock="客户端socket对象4"
# 新连接到来,执行 handler的 __init__ 方法
if sock == server_object:
print("新客户端来连接")
conn, addr = server_object.accept()
socket_object_list.append(conn)
# 实例化handler类,即:类(conn)
conn_handler_map[conn] = handler_class(conn)
continue
# 一旦有请求发来,找到相关的 handler对象,执行他的 execute方法。
# execute方法返回False,则意味着此客户端要断开连接。
handler_object = conn_handler_map[sock] # 自己PanHandler(conn1),
# 找到execute去处理各自的业务逻辑
result = handler_object.execute()
if not result:
socket_object_list.remove(sock)
del conn_handler_map[sock]
sock.close()
if __name__ == '__main__':
# server = Server()
server = SelectServer()
server.run(PanHandler)