• IO模型


    IO模型介绍:

    主进程的阻塞问题 ,多进程 多线程 只是分离了阻塞,而没有避免IO,因此有以下的模型:

    1.IO阻塞模型(blocking IO);

    2.非IO阻塞模型(nonblocking IO);

    3.IO多路复用(IO multiplexing);

    4.异步IO(asynchronous IO);用Python实现不了,是操作系统帮你的。

    5.信号驱动IO(signal driven IO  )不常用;

    IO阻塞模型的流程图:

    即就是平时我们所写的socket,accpet,recv,都需要等待,阻塞。

    2非IO阻塞模型的流程图:

    就是不断的去循环 询问操作系统是否有顺序;

    优点:CUP的利用率提高了;缺点:增加了CUP的负担;不推荐使用;

    基于非IO阻塞的socket

    server端:

    # 非阻塞IO 实际上就是在得到连接后就 循环的向操作系统询问 是否有数据,没有就可以做其他的事情(在这里实际上就是接收到其他的连接了,后
    # 循环的向操作系统询问 是否有数据)直达操作系统copy一份数据给进程。如果对面在传就在这样的接收,这样的过程就避免了IO阻塞等过程的
    # 时间,
    
    
    import socket
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    
    sk.setblocking(False)     #IO阻塞变为不阻塞了。
    sk.listen()
    
    conn_lsit = []
    
    while True:
    	try:
    		conn,addr = sk.accept()   #有连接就接收到,
    		conn_lsit.append(conn)      #为了防止如果有很多的连接的话,只能拿到最后一个,因为其他的被覆盖掉了。
    	except BlockingIOError:      #没有连接来的时候报错
    		del_list=[]
    		for conn in conn_lsit:
    			try:
    				ret = conn.recv(1024).decode('utf-8')     #尝试着接收数据,没有就继续向下执行
    				#当客户端连接关闭后,服务端就会接收空字符
    				if not ret:           #证明此连接传输完成,需要关闭此连接的服务端。
    					conn.close()
    					del_list.append(conn)            #这个连接已经接收到了数据。
    				else:
    					print(ret)    #正常的传输
    					msg = input('>>>')
    					conn.send(msg.encode('utf-8'))      #发送数据是自己发送的不会调用操作系统,因此不会阻塞。
    			except BlockingIOError:   #没有数据的时候报错
    				pass
    
    		if del_list:
    			for conn in del_list:
    				conn_lsit.remove(conn)        #为了在以后的循环中不再拿到已经关闭的连接
    
    
    	# conn.send(ret.upper())
    	#
    	# conn.close()
    	# sk.close()
    

     

    client端:

    import threading
    import socket
    
    def func():
    	sk = socket.socket()
    	sk.connect(('127.0.0.1', 8080))
    	sk.send(b'hello world')
    	ret = sk.recv(1024)
    	print(ret)
    	sk.close()
    
    for i in range(10):
    	threading.Thread(target=func).start()  #开启10个线程。
    

       

    3.IO多路复用的流程图

    用select模块来监听recv和accept,因此阻塞就变成了select;有数据的时候就通知;对于单个的对象所用的时间比IO阻塞要多,但是多个对象的时候效率就高了;

    优点:减少了CUP的负担,同时也能接收到数据,增加了CUP的利用率,一般多用这个模型;

     server端:

    import socket
    import select
    sk = socket.socket()
    sk.bind(('127.0.0.1',8099))
    sk.listen()
    
    read_lst = [sk]
    while True:
        rl,wl,xl = select.select(read_lst,[],[])   # select阻塞,rl可以读的 wl可以写的 xl可以改的 [sk,conn],
        # 当sk连接和conn连接同时有数据发来时 rl一个列表里面同时有两者
        # rl = [sk,coon]
        # 有数据的时候就会响应相应的sk或者是conn
        for item in rl:
            if item == sk:
                conn,addr = item.accept()  # 有数据等待着它接收
                read_lst.append(conn)
            else:
                ret = item.recv(1024).decode('utf-8')
                if not ret:                                  #在客户端关闭的时候会发送一个空字符
                    item.close()
                    read_lst.remove(item)
                else:
                    print(ret)
                    item.send(('received %s'%ret).encode('utf-8'))
    

      

    client端:

    import time
    import socket
    import threading
    def client_async(args):
        sk = socket.socket()
        sk.connect(('127.0.0.1',8099))
        for i in range(10):
            time.sleep(2)
            sk.send(('%s[%s] :hello'%(args,i)).encode('utf-8'))
            print(sk.recv(1024))
        sk.close()
    
    for i in range(10):
        threading.Thread(target=client_async,args=('*'*i,)).start()
    

      

     4.异步IO的流程图:

    操作系统帮你做数据准备阶段和数据copy阶段;用户在这期间可以做别的,操作系统拿到数据后就直接给你了。

     5中IO模型的比较图:

     从图中可以看出IO阻塞;非IO阻塞;以及IO多路复用 都避免不了数据的copy时间,而异步IO可以。

     readlst [sk,conn,conn2,conn3] 100 问一百次

    select ;poll 随着要检测的数据增加 效率会下降

    select 有数目的限制;

    poll 能处理的对象更多; 

    epoll 能处理多对象 不是使用轮换的询问; 而是拿到数据后就直接调用回调函数 。    

    epoll —— 但是只能在 linux执行,而epoll在windows下就不支持,好在我们有selectors模块,帮我们默认选择当前平台下最合适的;

    #服务端
    from socket import *
    import selectors
    
    sel=selectors.DefaultSelector()   # 创建一个默认的多路复用模型
    def accept(sk):
        conn,addr=sk.accept()
        sel.register(conn,selectors.EVENT_READ,read)
    
    def read(conn):
        try:
            data=conn.recv(1024)
            if not data:   #win8 win10
                print('closing',conn)
                sel.unregister(conn)
                conn.close()
                return
            conn.send(data.upper()+b'_SB')
        except Exception:    # linux操作系统
            print('closing', conn)
            sel.unregister(conn)
            conn.close()
    
    sk=socket(AF_INET,SOCK_STREAM)
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sk.bind(('127.0.0.1',8088))
    sk.listen(5)
    sk.setblocking(False) #设置socket的接口为非阻塞
    sel.register(sk,selectors.EVENT_READ,accept) #相当于往select的读列表里append了一个文件句柄server_fileobj,并且绑定了一个回调函数accept
    
    while True:
        events=sel.select() #检测所有的fileobj,是否有完成wait data的   #[sk,conn]
        for sel_obj,mask in events:   # 有人触动了你在sel当中注册的对象
            callback=sel_obj.data #callback=accpet   # sel_obj.data就能拿到当初注册的时候写的accept/read方法
            callback(sel_obj.fileobj) #accpet(sk)/read(conn)
    

      

  • 相关阅读:
    CentOS 6.6下 BCM4312 802.11b/g无线网卡驱动安装
    centOS6.6网络设置
    CentOS6.6安装(转)
    CCFlow工作流程起航
    LINQ to SQL 系列 如何使用LINQ to SQL插入、修改、删除数据 (转)
    LINQ to SQL 建立实体类 (转)
    字节数组的转换和合并
    ccflow之相对路径
    CCFlow SDK模式开发(有比较详细的代码,以服务的形式与ccflow数据库进行数据交互)
    Android从相册中获取图片以及路径
  • 原文地址:https://www.cnblogs.com/zenghui-python/p/10544246.html
Copyright © 2020-2023  润新知