最清晰的逻辑教你使用多进程实现TCP套接字词典查询(多并发/cs架构/MySQL/完整代码)

在线字典查询项目设计文档

功能要求

基本要求
用户可以登录和注册
登录凭借用户名和密码登录
注册要求用户必须填写用户名,密码,其他内容自定
用户名要求不能重复
要求用户信息能够长期保存
可以通过基本的图形界面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() 
数据库设计
table
table
table
database dict
words
user
history
id int primary key auto_increment
word valchar <32>
explain valchar 200
id int primary key auto_increment
name valchar 32
password valchar 128 加密
id int primary key auto_increment
name valchar 32
time datetime defalult now
模块设计
整体概述

整体设计使用类实现封装。客户端模块实现用户交互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)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值