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 
gevent_server.py
  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 
tcp_clent.py

gevent_server


二. 网络电子词典

  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()
worlds_dict.py
  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 
dict_server.py
  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()
dict_clent.py

words
cookie :
   1. 收集命令行参数为一个列表

       【1】 import sys
          【2】 sys.argv 可以将命令行输入参数收集为一个列表
          【3】 默认命令行以空格分隔每一项,如果一个整体中有空格则用引号注明一个整体
          【4】 收集的列表中所有项均为字符串


   2.使用getpass输入密码
   【1】import getpass
   【2】passwd=getpass.getpass(),其中getpass()函数用法与input相同,只是可以自动
     隐藏输入内容


作业 : 1. 整理进程线程网络内容
                 2. 复习mysql的理论内容和基本操作

原文地址:https://www.cnblogs.com/shengjia/p/10439690.html