在线字典查询项目设计文档
文章目录
功能要求
基本要求
用户可以登录和注册
登录凭借用户名和密码登录
注册要求用户必须填写用户名,密码,其他内容自定
用户名要求不能重复
要求用户信息能够长期保存
可以通过基本的图形界面print以提示客户端输入
程序分为服务端和客户端两部分
客户端通过print打印简单界面输入命令发起请求
服务端主要负责逻辑数据处理
启动服务端后应该能满足多个客户端同时操作
界面要求
客户端启动后即进入一级界面,包含如下功能:登录 注册 退出
退出后即退出该软件
登录成功即进入二级界面,失败回到一级界面
注册成功可以回到一级界面继续登录,也可以直接用注册用户进入二级界面
用户登录后进入二级界面,功能如下:查单词 历史记录 注销
选择注销则回到一级界面
查单词:循环输入单词,得到单词解释,输入特殊符号退出单词查询状态
历史记录:查询当前用户的查词记录,要求记录包含name word time。可以查看所有记录或者前10条均可。
需求分析
多并发方案—多进程
应对多个用户登录情况,可行方案主要有多进程、多线程、多路IO复用,均可选择,本例使用多进程Process实现
方案 | 实现方法 |
---|---|
多进程 | os模块fork()实现多进程: multiprocessing模块Process实现多进程: 1.为单独的入口函数开辟单独进程 2.自定义进程类,run定义启动函数,可以将共同使用的数据封装为类实例变量 |
多线程 | multiprocessing模块Tread实现多进程: 1.与Process进程模块使用方法相同 2.注意区别python线程和进程的区别 |
IO多路复用 | IO多路复用适合处理IO请求密集型问题,对于长期占用服务处理逻辑需要用多进程/线程 select–rs,ws,xs=select(rlist,wlist,xlist) rlist监听读属性IO,连接结束删除监听对象 poll—fdmap存放文件描述符与IO对象字典,register添加监听对象 epoll-- 与poll使用方法相同,但内部实现机制不同,epoll效率最高,可设置边缘触发 |
网络传输方法—TCP
使用TCP网络通信方式,特点是无丢失,无失序,无差错,无重复
技术细节
细节 | 实现 |
---|---|
注册信息 | 用户名,密码 |
历史记录 | 用户名,查询单词,查询时间----数据库查询最近10条记录:时间排序 |
密码 | 暗文输入:getpass模块getpass([prompt]) 存储加密:hashlib模块中的MD5加盐加密 |
# MD5加密算法调用
hash=hashlib.md5("salt**@来干小日本!".encode()) # 参数为加盐内容,可缺省
password="123456"
hash.update(password.encode())
secret=hash.hexdigest()
数据库设计
模块设计
整体概述
整体设计使用类实现封装。客户端模块实现用户交互interface,负责用户输入,结果呈现等基本功能;服务器模块采用多进程并发处理客户端连接请求;数据库模块处理数据读写,包括加码处理等;
客户端模块- client
class OnlineDictClient():
def __init__(self): # 客户端连接套接字初始化
def mian(self): # 登录注册(一级界面)
def regist(self): # 用户注册
def login(self): # 用户登录
def dict(self,name): # 主要功能界面:查询,历史记录(二级界面)
def search(self,name): # 查询
def history(self,name): # 历史记录查看
服务端模块- server
class OnlineDictServer():
def __init__(self): # 数据库对象,监听套接字初始化
def main(self): # 循环监听,分配子进程
def handle(self,client): # 子进程处理主逻辑,解析客户端请求内容
def regist(self, client,request): # 用户注册逻辑处理
def login(self,client,info_log): # 用户登录逻辑处理
def search(self,client,request): # 用户查询字典逻辑处理
def history(self,client,request): # 用户查看历史记录逻辑处理
数据库操作模块- db_operate
import pymysql,hashlib
class DataBase():
def __init__(self): # 初始化,连接数据库
def secret(self,pwd): # 密码加密,hashlib模块MD5处理密码
def register(self, name, pwd):# 用户注册,数据库写操作-->table user:name password
def login(self,name,pwd): # 用户登录,数据库读操作<--select from user
def search(self,name,word): # 用户查询,读单词解释<--select words:explain
def inserthistory(self,name,word): # 用户查询记录写入,name,word和time-->history
def history(self,name): # 历史记录查看,返回最近10条记录<--select+order by+limit 10
完整代码
客户端(client.py)
from socket import *
import pymysql
from getpass import getpass
import sys
class OnlineDictClient():
# 客户端连接套接字初始化
def __init__(self):
self.ADDR=("127.0.0.1",5200) #服务器地址
self.socketclient=socket()
self.socketclient.connect(self.ADDR)
# 登录注册(一级界面)
def mian(self):
while True:
print("""
=======Online Dict=======
1.注册 2.登录 3.退出
=========================""")
cmd=input("输入选项:")
if cmd not in ("1","2","3"):
print("请输入正确选项!")
continue
# 直接将选项作为请求格式,避免再次拟定发送格式
if cmd=="1": # 注册
self.regist()
elif cmd=="2": # 登录
self.login()
elif cmd=="3": # 退出
sys.exit("谢谢使用!")
# 用户注册
def regist(self):
while True:
name=input("请输入名称:")
# pwd=input("请输入密码:")
# pwdc=input("请再次确认密码:")
pwd=getpass()
pwdc=getpass("Again:")
if pwd!=pwdc:
print("输入密码不一致")
continue
if " " in name or " " in pwd:
print("输入用户名或密码不合法,包含空格")
continue
s="R %s %s"%(name,pwd)
self.socketclient.send(s.encode())
confirm=self.socketclient.recv(1024)
if confirm==b"OK":
print("注册成功")
break
else:
print("注册失败,用户名已存在!")
# 用户登录
def login(self):
while True:
print("""
=======Online Dict=======
用户登录界面
=========================
""")
name = input("请输入您的名称:")
pwd = input("请输入您的密码:")
s="L %s %s"%(name,pwd)
self.socketclient.send(s.encode())
info_cfrm=self.socketclient.recv(1024)
if info_cfrm==b"OK":
print("恭喜登陆成功!")
self.dict(name)
break
else:
print("用户名或密码错误,请重新输入!")
# 主要功能界面:查询,历史记录(二级界面)
def dict(self,name):
while True:
print("""
=======Online Dict=======
1.英语单词查询
2.历史记录查看
3.注销
=========================
""")
cmd=input("请输入选项:")
if cmd == "1": # 查询
self.search(name)
elif cmd == "2": # 历史记录
self.history(name)
elif cmd == "3": # 退出
return
# 查询
def search(self,name):
self.socketclient.send(("S %s"%name).encode())
while True:
word =input("请输入查询单词:")
if not word:
self.socketclient.send(b"EXIT")
break
self.socketclient.send(word.encode())
explain=self.socketclient.recv(1024)
if explain==b"404":
print("无此单词")
else:
print(explain.decode())
# 历史记录查看
def history(self,name):
self.socketclient.send(("H %s"%name).encode())
history=self.socketclient.recv(2048)
print("您的历史记录:\n",history.decode())
if __name__=="__main__":
client=OnlineDictClient()
client.mian()
服务器(server.py)
from socket import *
import pymysql
from multiprocessing import *
from signal import *
from db_operate import *
import sys
class OnlineDictServer():
# 数据库对象,监听套接字初始化
def __init__(self):
# 数据库对象调用
self.db=DataBase()
# 套接字初始化
self.port=5200
self.ip="127.0.0.1"
self.ADDR=(self.ip,self.port) #服务器地址
self.socketfd=socket()
self.socketfd.bind(self.ADDR)
self.socketfd.listen(5)
print("Listen from port:%d"%self.port)
self.socketfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
signal(SIGCHLD,SIG_IGN)
# 循环监听,分配子进程
def main(self):
print("")
while True:
try:
conn,addr=self.socketfd.accept()
print("connect from",addr)
except Exception:
print("服务器退出!")
self.p=Handle(conn,self.db)
self.p.start()
class Handle(Process):
def __init__(self,conn,db):
self.conn=conn
self.db=db
super().__init__()
# 子进程处理主逻辑,解析客户端请求内容
def run(self):
while True:
request=self.conn.recv(1024).decode()
if not request or request[0]=="E":
sys.exit(0)
elif request[0] == "R":
self.regist(request)
elif request[0] == "L":
self.login(request)
elif request[0]=="S":
self.search(request)
elif request[0] == "H":
self.history(request)
# 用户注册逻辑处理
def regist(self, request):
name=request.split(" ")[1]
pwd=request.split(" ")[-1]
if self.db.register(name,pwd):
self.conn.send(b"OK")
else:
self.conn.send(b"name exist")
# 用户登录逻辑处理
def login(self,info_log):
name = info_log.split(" ")[1]
pwd = info_log.split(" ")[-1]
if self.db.login(name,pwd):
self.conn.send(b"OK")
else:
self.conn.send(b"incorrect")
# 用户查询字典逻辑处理
def search(self,request):
name=request.split(" ")[-1]
while True:
word=self.conn.recv(1024).decode()
if word=="EXIT":
break
explain=self.db.search(name,word)
if explain:
self.conn.send(explain.encode())
else:
self.conn.send(b"404")
# 用户查看历史记录逻辑处理
def history(self,request):
name=request.split(" ")[-1]
records=self.db.history(name)
if records:
self.conn.send(records.encode())
else:
self.conn.send(("%s have no search history"%name).encode())
if __name__=="__main__":
server=OnlineDictServer()
server.main()
数据库操作(dboperate.py)
import pymysql
import hashlib
class DataBase():
# 初始化,连接数据库
def __init__(self):
self.db=pymysql.connect(
host="localhost",
port=3306,
user="root",
password="123456",
database="dict",
charset='utf8' )
# 游标
self.cur = self.db.cursor()
# 密码加密,hashlib模块MD5处理密码
def secret(self,pwd):
hash=hashlib.md5("salt**来干小日本!".encode())
hash.update(pwd.encode())
return hash.hexdigest()
# 用户注册,数据库写操作-->table user:name password
def register(self, name, pwd):
# 对密码进行加密处理
pwd=self.secret(pwd)
# 判断是否存在该用户名
sql = "select name from user where name='%s';" % name
self.cur.execute(sql)
if self.cur.fetchone():
return False
# 插入记录
try:
sql = "insert into user (name,password) values ('%s','%s')" % (name, pwd)
self.cur.execute(sql)
# 写操作
self.db.commit()
return True
except:
self.db.rollback()
return False
# 用户登录,数据库读操作<--select from user
def login(self,name,pwd):
pwd = self.secret(pwd)
sql="select * from user where name='%s' and password='%s'"%(name,pwd)
self.cur.execute(sql)
if self.cur.fetchone():
return True
else:
return False
# 用户查询,插入查询name,word和time-->history
def inserthistory(self,name,word):
sql="insert into history (name,word) values ('%s','%s')"%(name,word)
try:
self.cur.execute(sql)
self.db.commit()
except:
self.db.rollback()
# 用户查询,记录,读单词解释<--select words:explain
def search(self,name,word):
self.inserthistory(name,word)
sql = "select wordexplain from words where word='%s'" % word
self.cur.execute(sql)
# 返回结果为元组
explain=self.cur.fetchone()
if explain:
return explain[0]
# 历史记录查看,返回最近10条记录
def history(self,name):
sql = "select * from history where name=%s order by searchtime desc limit 10"
self.cur.execute(sql,[name])
records=self.cur.fetchall()
str_=""
for record in records:
str_+=(str(record)+"\n")
return str_
if __name__=="__main__":
db=DataBase()
res=db.history("lei")
print(res)