zoukankan      html  css  js  c++  java
  • python入门第三十五天--事件驱动模型(补)练习理解

    阻塞IO

    阻塞IO(blocking IO)的特点:就是在IO执行的两个阶段(等待数据和拷贝数据两个阶段)都被block了。

     什么是阻塞呢?想象这种情形,比如你等快递,但快递一直没来,你会怎么做?有两种方式:

    • 快递没来,我可以先去睡觉,然后快递来了给我打电话叫我去取就行了。
    • 快递没来,我就不停的给快递打电话说:擦,怎么还没来,给老子快点,直到快递来。

    很显然,你无法忍受第二种方式,不仅耽搁自己的时间,也会让快递很想打你。
    而在计算机世界,这两种情形就对应阻塞和非阻塞忙轮询。

      • 非阻塞忙轮询:数据没来,进程就不停的去检测数据,直到数据来。
      • 阻塞:数据没来,啥都不做,直到数据来了,才进行下一步的处理

    server

     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket
     8 
     9 sk=socket.socket()
    10 sk.bind(('127.0.0.1',8080))
    11 sk.listen(3)
    12 
    13 while 1:
    14     conn,addr=sk.accept()
    15     while 1:
    16         data=conn.recv(1024)
    17         print(data.decode('utf8'))
    18         conn.sendall(data)

    client

    #!/usr/bin/env python3
    #-*- coding:utf-8 -*-
    '''
    Administrator 
    2018/9/3 
    '''
    import socket
    
    sk=socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    while 1:
        inp=input(">>>")
        sk.sendall(inp.encode('utf8'))
        data=sk.recv(1024)
        print(data.decode('utf8'))

    运行结果:

    "D:Program Files (x86)python36python.exe" F:/python从入门到放弃/9.3/client.py
    >>>hello
    hello
    >>>nihao
    nihao
    >>>中国
    中国
    >>>亚运会
    亚运会
    >>>中国金牌数
    中国金牌数
    >>>

    非阻塞型IO模型

    非阻塞式IO中,用户进程其实是需要不断的主动询问kernel数据准备好了没有

    非阻塞如何利用

    • 吃满 CPU !
    • 宁可用 while True ,也不要阻塞发呆!
    • 只要资源没到,就先做别的事!

    server:

     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket,time
     8 #非阻塞型IO模型
     9 sk=socket.socket()
    10 sk.bind(('127.0.0.1',8080))
    11 sk.listen(3)
    12 
    13 sk.setblocking(False)#设置非阻塞。 IO模型发生了变化
    14 print("start listen")
    15 conn_list=[]#连接列表
    16 while 1:
    17     try:
    18         conn, addr = sk.accept()
    19         print("connet by",addr)
    20         conn_list.append(conn)
    21         conn.setblocking(False)#设置非阻塞。 IO模型发生了变化
    22         # while 1:
    23         #     data=conn.recv(1024)
    24         #     print(data.decode('utf8'))
    25         #     conn.sendall(data)
    26     except Exception as e:
    27         print("没有接收到数据。警告:%s"%e)
    28         time.sleep(3)
    29 
    30     for conn in conn_list:
    31         try:
    32             data=conn.recv(1024)
    33             if data:
    34                 print(data.decode('utf8'))
    35                 conn.sendall(data)
    36             else:
    37                 print('close conn',conn)
    38                 conn.close()
    39                 conn_list.remove(conn)
    40                 print("还有在线的客户端个数:%s"%len(conn_list))
    41         except IOError:
    42             pass

    client:

     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket
     8 
     9 sk=socket.socket()
    10 
    11 sk.connect(('127.0.0.1', 8080))
    12 while 1:
    13     inp=input(">>>")
    14     if inp != 'q':
    15         sk.sendall(inp.encode('utf8'))
    16         data=sk.recv(1024)
    17         print("返回的数据%s"%data.decode('utf8'))
    18     else:
    19         sk.close()
    20         print("关闭客户端")
    21         break#跳出循环

    运行结果:

    "D:Program Files (x86)python36python.exe" F:/python从入门到放弃/9.3/server.py
    start listen
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    connet by ('127.0.0.1', 54241)
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    connet by ('127.0.0.1', 54242)
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    connet by ('127.0.0.1', 54243)
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    这是1个
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    这是2个
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    这是3个
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    close conn <socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 54241)>
    还有在线的客户端个数:2
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    close conn <socket.socket fd=232, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 54242)>
    还有在线的客户端个数:1
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    close conn <socket.socket fd=248, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 54243)>
    还有在线的客户端个数:0
    没有接收到数据。警告:[WinError 10035] 无法立即完成一个非阻止性套接字操作。
    
    Process finished with exit code 1

    多路复用IO

    把socket交给操作系统去监控,相当于找个代理人(select), 去收快递。快递到了,就通知用户,用户自己去取。

    阻塞I/O只能阻塞一个I/O操作,而I/O复用模型能够阻塞多个I/O操作,所以才叫做多路复用

    使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,感觉效率更差。

    但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,

    即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

    epoll是目前Linux上效率最高的IO多路复用技术。

    epoll是惰性的事件回调,惰性事件回调是由用户进程自己调用的,操作系统只起到通知的作用。

    epoll实现并发服务器,处理多个客户端




     1 import socket
     2 import select
     3 
     4 sk1=socket.socket()
     5 sk1.bind(('127.0.0.1',8080))
     6 sk1.listen(3)
     7 
     8 sk2=socket.socket()
     9 sk2.bind(('127.0.0.1',8081))
    10 sk2.listen(3)
    11 while 1:
    12     r,w,e=select.select([sk1,sk2],[],[])#监听--------读 ,写 ,错误
    13     for obj in r:
    14         conn,address=obj.accept()
    15         # obj.recv(1024)
    16         conn.send("i am server...".encode('utf8'))
    server
     1 import socket
     2 import time
     3 
     4 sk=socket.socket()
     5 
     6 
     7 while 1:
     8     sk.connect(('127.0.0.1', 6667))
     9     print("ok")
    10     sk.sendall(bytes("hello","utf8"))
    11     time.sleep(2)
    12     break
    client

     1 import socket
     2 import select
     3 sk=socket.socket()
     4 sk.bind(("127.0.0.1",8800))
     5 sk.listen(5)
     6 
     7 sk1=socket.socket()
     8 sk1.bind(("127.0.0.1",6667))
     9 sk1.listen(5)
    10 
    11 while True:
    12     r,w,e=select.select([sk,sk1],[],[],5)
    13     for i in r:#r 接受到的是绑定的两个socket 对象之一。是服务端的对象  conn 是这两个r对象连接的客户端的对象
    14         # conn,add=i.accept()
    15         # print(conn)
    16         print("hello")
    17     print('>>>>>>',r)
     1 import socket
     2 import time
     3 
     4 sk=socket.socket()
     5 
     6 
     7 while 1:
     8     sk.connect(('127.0.0.1', 6667))
     9     print("ok")
    10     sk.sendall(bytes("hello","utf8"))
    11     time.sleep(2)
    12     break

    运行结果:

    ...........重复打印.........
    hello
    >>>>>> [<socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6667)>]
    hello
    >>>>>> [<socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6667)>]
    hello
    >>>>>> [<socket.socket fd=216, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6667)>]
    
    ...................
    select属于水平触发
    触发方式:水平触发,边缘触发。
    水平状态,有 就触发,没有就不触发 1触发 0不触发
    边缘触发,有变化就触发,没有变化就不触发。0->1触发 1->0触发




     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket
     8 import select
     9 
    10 sk=socket.socket()
    11 sk.bind(('127.0.0.1',8800))
    12 sk.listen(5)
    13 
    14 
    15 inp=[sk,]
    16 while 1:
    17     inputs,outputs,errors=select.select(inp,[],[],5)
    18     for obj in inputs:
    19         if obj==sk:
    20             conn,addr=obj.accept()
    21             print(conn)
    22             inp.append(conn)
    23             # data=conn.recv(1024)
    24             # print(data.decode('utf8'))
    25             # conn.sendall(data)
    26         else:
    27             data = obj.recv(1024)
    28             print(data.decode('utf8'))
    29             inps=input("回答%s>>>"%inp.index(obj))
    30             obj.sendall(inps.encode("utf8"))

    客户端:

     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket
     8 import time
     9 
    10 sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    11 
    12 sk.connect(('127.0.0.1', 8800))
    13 while 1:
    14     inp=input(">>>").strip()
    15     sk.sendall(bytes(inp,"utf8"))
    16     data=sk.recv(1024)
    17     print(str(data,"utf8"))


    改进::: 当有用户退出时,不影响服务端的聊天通信::

     1 #!/usr/bin/env python3
     2 #-*- coding:utf-8 -*-
     3 '''
     4 Administrator 
     5 2018/9/3 
     6 '''
     7 import socket
     8 import select
     9 
    10 sk=socket.socket()
    11 sk.bind(('127.0.0.1',8800))
    12 sk.listen(5)
    13 
    14 
    15 inp=[sk,]
    16 while 1:
    17     inputs,outputs,errors=select.select(inp,[],[],5)
    18     for obj in inputs:
    19         if obj==sk:
    20             conn,addr=obj.accept()
    21             print(conn)
    22             inp.append(conn)
    23             # data=conn.recv(1024)
    24             # print(data.decode('utf8'))
    25             # conn.sendall(data)
    26         else:
    27             try:
    28                 data = obj.recv(1024)#如果断开联系,则把该对象从inp列表中删除
    29             except Exception:
    30                 inp.remove(obj)
    31             if obj in inp:#筛选一下
    32                 print(data.decode('utf8'))
    33                 inps=input("回答%s>>>"%inp.index(obj))
    34                 obj.sendall(inps.encode("utf8"))






  • 相关阅读:
    一步一步教你使用AgileEAS.NET基础类库进行应用开发基础篇演示ORM中的查询
    一步一步教你使用AgileEAS.NET基础类库进行应用开发WinForm应用篇实例一个模块(商品字典)
    DotNET企业架构应用实践基于接口开发介绍以及应用场景和案例
    DotNET企业架构应用实践实例架构设计中的业务分层提取独立的业务层
    如何去除Visual SourceSafe绑定信息
    死机也新潮,Longhorn出现致命红屏?
    推荐一套MFC界面控件(BCGControlBar Library v6.50)
    从键盘磨损度看一个人(绝对精典搞笑)(转载)
    加速你的开发工具【代码片断库插件】
    空间插值方法汇总(转载但已记不得出处了)
  • 原文地址:https://www.cnblogs.com/Mengchangxin/p/9577479.html
Copyright © 2011-2022 走看看