PYTHON2.day10
前情回顾
1. 多线程并发网络模型
2. 基于Process的多进程并发网络
3. 集成模块socketserver完成网络并发
4. HTTPServer v2.0:模块封装,多线程并发,请求解析
5. 协程基础 : 定义,原理,优缺点
6. 介绍greenlet,学习gevent
【1】 gevent.spawn() 生成协程对象
【2】 gevent.joinall() 阻塞等待协程执行完成
【3】 gevent.sleep() 提供gevent阻塞
*********************************************************
一. gevent模块(续)
1. 动机:在gevent协程中,协程只有遇到gevent指定类型的阻塞才能跳转到其他协程,因此,我们希望将普通的IO阻塞行为转换为可以触发gevent协程跳转的阻塞,以提高执行效率。
2. 转换方法:gevent 提供了一个脚本程序monkey,可以修改底层解释IO阻塞的行为,将很多普通阻塞转换为gevent阻塞。
【1】 导入monkey
from gevent import monkey
【2】 运行相应的脚本,例如转换socket中所有阻塞
monkey.patch_socket()
【3】 如果将所有可转换的IO阻塞全部转换则运行all
monkey.patch_all()
【4】 注意:脚本运行函数需要在对应模块导入前执行
1 import gevent 2 from gevent import monkey 3 monkey.patch_all()#执行脚本插件,修改IO阻塞行为 4 from socket import * 5 6 #创建套接字 7 def server(): 8 s = socket() 9 s.bind(('0.0.0.0',8888)) 10 s.listen(10) 11 while True: 12 c,addr = s.accept()#主程序遇到阻塞触发协程 13 print("Conect from",addr)#打印客户端 14 # handle(c)#处理客户端请求 15 gevent.spawn(handle,c)#把函数变成协程,c->不定参数}-----协程方案,多客户端 16 17 def handle(c): 18 while True: 19 data = c.recv(1024)#协程阻塞在recv 20 if not data: 21 break 22 print(data.decode()) 23 c.send(b"OK") 24 25 server() 26
1 from socket import * 2 3 #创套接字 4 sockfd = socket() 5 6 #发起连接 7 server_addr = ('172.40.71.149',8888) 8 sockfd.connect(server_addr) 9 10 #收发消息 11 while True: 12 data = input(">>") 13 if not data: 14 break 15 sockfd.send(data.encode()) 16 data = sockfd.recv(1024) 17 print("From server:",data.decode()) 18 19 #关闭套接子 20 sockfd.close() 21
二. 网络电子词典
1. 功能说明
【1】用户可以登录和注册
* 登录凭借用户名和密码登录
* 注册要求用户必须填写用户名,密码,其他内容自定
* 用户名要求不能重复
* 要求用户信息能够长期保存
【2】可以通过基本的图形界面print以提示客户端输入。
* 程序分为服务端和客户端两部分
* 客户端通过print打印简单界面输入命令发起请求
* 服务端主要负责逻辑数据处理
* 启动服务端后应该能满足多个客户端同时操作
【3】客户端启动后即进入一级界面,包含如下功能:
登录 注册 退出
* 退出后即退出该软件
* 登录成功即进入二级界面,失败回到一级界面
* 注册成功可以回到一级界面继续登录,也可以直接用注册用户进入二级界面
【4】用户登录后进入二级界面,功能如下:
查单词 历史记录 注销
* 选择注销则回到一级界面
* 查单词:循环输入单词,得到单词解释,输入特殊符号退出单词查询状态
* 历史记录:查询当前用户的查词记录,要求记录包含name word time。可以查看所有记录或者前10条均可。
2. 单词本说明
【1】 特点 : 1. 每个单词一定占一行
2. 单词按照从小到大顺序排列
3. 单词和解释之间一定有空格
【2】 查词说明 : 1. 直接使用单词本查询(文本操作)
2. 先将单词存入数据库,然后通过数据库查询。(数据库操作)
3. 操作步骤
【1】 确定并发方案? 确定套接字使用? 确定具体细节?
使用文件查询还是数据库?
* fork 多进程 ,tcp套接字
* 注册后回到一级界面,历史记录显示最近10条
* 文本直接查询
【2】 建立数据库 : 建立几个表,表关系,表字段及类型
* 想办法将单词导入数据库
用户表 : id name passwd
历史记录:id name word time
单词存储:id word mean
1. 创建数据库:
create database dict default charset=utf8;
2. 创建用户表:
create table user (id int primary key auto_increment,name varchar(32) not null,passwd varchar(16) default '000000');
3. 创建历史记录表:
create table hist (id int primary key auto_increment,name varchar(32) not null,word varchar(32) not null,time varchar(64));
4. 创建单词表:
create table words (id int primary key auto_increment,word varchar(32),mean text);
【3】 结构设计:即如何封装,客户端和服务端工作流程。具体项目有几个功能模块。
* 函数封装
* 客户端启动--》进入一级界面--》登录--》二级界面--》具体请求--》展示内容
* 服务端循环接收请求--》处理请求--》将数据发送给客户端
* 功能模块:登录,注册,查询单词,历史记录
【4】 完成通信的搭建
【5】 分析具体通能,逐个模块实现
1、注册
客户端:*输入注册信息
*将信息发送给服务端
*得到服务器反馈
服务端:*接收请求
*判断是否允许注册
*反馈结果给客户端
*如果可以注册则插入数据库
2. 登录
客户端: * 输入用户名密码
* 将信息发送给服务器
* 得到服务端反馈
* 如果登录成功进入二级界面
服务端: * 接收请求
* 判断是否允许登录
* 反馈结果
3. 查词
客户端 : * 输入查询单词
* 发送请求给服务端
* 获取结果
服务端 : * 接收请求
* 查找单词
* 将查询结果发送给客户端
* 插入历史记录
4. 历史记录
1 import pymysql 2 3 f = open('dict.txt') 4 db = pymysql.connect('localhost','root','123456','dict') 5 6 cursor = db.cursor() #创建游标 7 8 for line in f: 9 tmp = line.split(' ') 10 world = tmp[0] 11 mean = ' '.join(tmp[1:]).strip() 12 sql='insert into worlds (world,mean) values ("%s","%s")'%(world,mean) 13 14 try: 15 cursor.execute(sql) 16 db.commit() 17 except Exception: 18 db.rollback() 19 f.close()
1 ''' 2 dict project for AID 3 ''' 4 from socket import * 5 import pymysql 6 import os,sys 7 import time#处理沾包 8 import signal#处理僵尸 9 10 #定义全局变量 11 if len(sys.argv)<3: 12 print('''Start as: 13 python3 dict_server.py 0.0.0.0 8000 14 ''') 15 sys.exit(0) 16 17 HOST = sys.argv[1] 18 PORT = int(sys.argv[2]) 19 ADDR = (HOST,PORT) 20 DICT_TEXT = "./dict.txt" 21 22 #搭建网络连接 23 def main(): 24 #连接数据库 25 db = pymysql.connect('localhost','root','123456','dict') 26 #创建套接字 27 s = socket() 28 # s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 29 s.bind(ADDR) 30 s.listen(5) 31 32 33 #僵尸进程处理 34 signal.signal(signal.SIGCHLD,signal.SIG_IGN) 35 36 37 while True: 38 try: 39 c,addr = s.accept() 40 print("Connect from",addr) 41 except KeyboardInterrupt: 42 s.close() 43 sys.exit("服务器退出") 44 except Exception as e: 45 print(e) 46 continue 47 48 #创建子进程 49 pid = os.fork() 50 if pid ==0: 51 s.close() 52 do_child(c,db) 53 sys.exit() 54 else: 55 c.close() 56 57 58 59 60 61 #处理客户端请求 62 def do_child(c,db): 63 while True: 64 #接收客户端请求 65 data = c.recv(1024).decode() 66 print(c.getpeername(),':',data) 67 if not data or data[0]=="E": 68 c.close() 69 sys.exit() 70 elif data[0]=='R': 71 do_register(c,db,data) 72 elif data[0]=='L': 73 do_login(c,db,data) 74 elif data[0]=='Q': 75 do_query(c,db,data) 76 elif data[0]=='H': 77 do_hist(c,db,data) 78 79 80 81 82 83 84 def do_register(c,db,data): 85 l = data.split(' ') 86 name =l[1] 87 passwd = l[2] 88 cursor = db.cursor() 89 90 sql ="select * from user where name='%s'"%name 91 cursor.execute(sql) 92 r = cursor.fetchone() 93 if r != None: 94 c.send(b'EXITS') 95 return 96 97 #插入用户 98 sql = "insert into user(name,passwd) values('%s','%s')"%(name,passwd) 99 try: 100 cursor.execute(sql) 101 db.commit() 102 c.send(b'OK') 103 except: 104 db.rollback() 105 c.send(b'FAIL') 106 107 108 109 def do_login(c,db,data): 110 l = data.split(' ') 111 name = l[1] 112 passwd = l[2] 113 cursor = db.cursor() 114 115 116 sql = "select * from user where name='%s' and passwd='%s'"%(name,passwd) 117 #查询用户 118 cursor.execute(sql) 119 r = cursor.fetchone() 120 if r ==None: 121 c.send(b'FAIL') 122 else: 123 c.send(b'OK') 124 125 126 127 128 129 def do_query(c,db,data): 130 l = data.split(' ') 131 name = l[1] 132 world = l[2] 133 134 135 #插入历史记录 136 cursor = db.cursor() 137 tm = time.ctime() 138 sql = "insert into hist(name,world,time) values ('%s','%s','%s')"%(name,world,tm) 139 try: 140 cursor.execute(sql) 141 db.commit() 142 except: 143 db.rollback() 144 145 #单词本查找 146 f = open(DICT_TEXT) 147 148 for line in f: 149 tmp = line.split(' ')[0]#获取单词 150 if tmp > world: 151 break 152 elif tmp == world: 153 c.send(line.encode()) 154 return 155 c.send("没有找到该单词") 156 f.close() 157 158 159 def do_hist(c,db,data): 160 name = data.split(' ')[1] 161 cursor = db.cursor() 162 sql = "select * from hist where name='%s' order by id desc limit 10"%name 163 cursor.execute(sql) 164 r = cursor.fetchall() 165 if not r: 166 c.send(b'FAIL') 167 return 168 else: 169 c.send(b'OK') 170 time.sleep(0.1) 171 for i in r: 172 msg = "%s %s %s"%(i[1],i[2],i[3]) 173 c.send(msg.encode()) 174 time.sleep(0.1) 175 c.send(b'##') 176 177 178 179 180 if __name__=="__main__": 181 main() 182
1 from socket import * 2 import sys 3 import getpass 4 5 #创建网络连接 6 def main(): 7 if len(sys.argv) <3: 8 print("argv is error") 9 return 10 HOST = sys.argv[1] 11 PORT = int(sys.argv[2]) 12 s = socket() 13 try: 14 s.connect((HOST,PORT)) 15 except Exception as e: 16 print(e) 17 return 18 while True: 19 print(''' 20 ===============================wecome============================== 21 --1.注册 2.登录 3.退出-- 22 ''') 23 try: 24 cmd = int(input("输入选项:")) 25 except Exception as e: 26 print("命令错误") 27 continue 28 if cmd not in [1,2,3]: 29 print("请输入正确选项") 30 continue 31 elif cmd ==1: 32 do_register(s) 33 elif cmd ==2: 34 do_login(s) 35 elif cmd ==3: 36 s.send(b'E') 37 sys.exit("谢谢使用") 38 39 def do_register(s): 40 while True: 41 name = input("User:") 42 passwd = getpass.getpass() 43 passwd1= getpass.getpass("Again:") 44 45 if (' 'in name) or (' ' in passwd): 46 print("用户名密码不能有空格") 47 continue 48 if passwd != passwd1: 49 print("两次密码不一致") 50 continue 51 52 msg = "R %s %s"%(name,passwd) 53 #发送请求 54 s.send(msg.encode()) 55 #等待回复 56 data = s.recv(128).decode() 57 if data =='OK': 58 print("注册成功") 59 # login(s,name)#注册成功进入二级界面 60 elif data =='EXISTS': 61 print("用户已存在") 62 else: 63 print("注册失败") 64 return 65 66 67 68 def do_login(s): 69 name = input("User:") 70 passwd = getpass.getpass() 71 msg = "L %s %s"%(name,passwd) 72 s.send(msg.encode()) 73 data = s.recv(128).decode() 74 if data =="OK": 75 print("登录成功") 76 login(s,name) 77 else: 78 print("登录失败") 79 80 def login(s,name): 81 while True: 82 print(''' 83 ===============================wecome============================== 84 --1.查词 2.历史记录 3.注销-- 85 ''') 86 try: 87 cmd = int(input("输入选项:")) 88 except Exception as e: 89 print("命令错误") 90 continue 91 if cmd not in [1,2,3]: 92 print("请输入正确选项") 93 continue 94 elif cmd ==1: 95 do_query(s,name) 96 elif cmd ==2: 97 do_hist(s,name) 98 elif cmd ==3: 99 return #回到一级界面 100 101 def do_query(s,name): 102 while True: 103 world = input("单词:") 104 if world == '##': 105 break 106 msg ='Q %s %s'%(name,world) 107 s.send(msg.encode()) 108 #可能是单词解释.也可能是找不到 109 data = s.recv(2048).decode() 110 print(data) 111 112 def do_hist(s,name): 113 msg = "H %s"%name 114 s.send(msg.encode()) 115 data = s.recv(128).decode() 116 if data =="OK": 117 while True: 118 data = s.recv(1024).decode() 119 if data =="##": 120 break 121 print(data) 122 else: 123 print("没有历史记录") 124 125 126 127 128 129 130 131 132 if __name__=="__main__": 133 main()
【1】 import sys
【2】 sys.argv 可以将命令行输入参数收集为一个列表
【3】 默认命令行以空格分隔每一项,如果一个整体中有空格则用引号注明一个整体
【4】 收集的列表中所有项均为字符串
2.使用getpass输入密码
【1】import getpass
【2】passwd=getpass.getpass(),其中getpass()函数用法与input相同,只是可以自动
隐藏输入内容
作业 : 1. 整理进程线程网络内容
2. 复习mysql的理论内容和基本操作