035_IO 模型

IO 模型分類linux

    * blocking IO           阻塞IO
    * nonblocking IO      非阻塞IO
    * IO multiplexing      IO多路複用
    * signal driven IO     信號驅動IO
    * asynchronous IO    異步IOwindows

1,阻塞IO 的圖網絡

  recv數據多線程

  阻塞IO併發

  非阻塞IOapp

 

  IO多路複用異步

2,socket

  阻塞IO    : 工做效率低
  非阻塞IO  : 工做效率高,頻繁詢問切換加大CPU的負擔
  IO多路複用: 在有多個對象須要IO阻塞的時候,可以有效的減小阻塞帶來的時間損耗,且可以在必定程度上減小CPU的負擔
  異步IO : asyncio  異步IO  工做效率高 CPU的負擔少async

3,網絡IOide

       # recv     recvfrom   accept   requests.get()
       # send    connect    sendto
4,IO的兩個階 段
       # 數據準備階段
       # 數據copy階段
 
解決阻塞IO 問題
   1)多進程 多線程 分離了阻塞
    但進程線程不能無限開!
   2)之後用進程都用進程池
    單純的進程池不能知足用戶的需求,只適合小併發的問題,真正須要咱們解決的是I/O問題
1,非阻塞IO實例
import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() sk.setblocking(False) # 異步的,可是來不及打開client端創建鏈接,就已經報錯 # 由於下一句不阻塞了 # recv 也不阻塞了,但同樣會報錯 ,有鏈接時,且設爲不阻塞時,沒消息就打印空消息。 # 緣由:調用阻塞異常錯誤(變量沒有被賦值)
conn,addr = sk.accept() print(conn)
sever端
import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() sk.setblocking(False) conn_lst = []    # 用來保存已經創建的鏈接

while True: try: conn,addr = sk.accept()   #非阻塞 
        conn_lst.append(conn)   # 不將創建成功的鏈接用列表保存下來,conn會不斷被新鏈接覆蓋。
    except BlockingIOError: del_lst = [] for c in conn_lst: try: msg = c.recv(10).decode('utf-8')  # recv不會阻塞
                if not msg: c.close() del_lst.append(c) else: print(msg) c.send(msg.upper().encode('utf-8')) except BlockingIOError: pass
        if del_lst: for del_item in del_lst: conn_lst.remove(del_item)
sever端改進
import time import socket import threading def func(): sk = socket.socket() sk.connect(('127.0.0.1',8080)) time.sleep(1) sk.send(b'hi') print(sk.recv(10)) sk.close() for i in range(10): threading.Thread(target=func,).start()
client端

多路複用

# 操做系統提供的多路複用機制
      #  select  、 poll 、  epoll、
      # windows 上只有 select
      # linux 兼容三種

  # 原來是recv,如今是交給select ;select([recv])

1,參數

  select(rlist,wlisr,xlist,timeout = None)
            等能  讀,寫,改   三個參數是列表,必須傳參,沒有參數傳空列表。返回的結果是一個有三個列表的元組,分別是讀,寫,改的結果。

2,實例

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()
client端
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]
    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'))
sever 端

3,

# select poll 隨着要檢測的數據增長 效率會降低,不適合處理大併發。
# select  有數目的限制
# poll    能處理的對象更多
# epoll   能處理多對象且 不是使用輪詢  而是用回調函數 —— linux
#這三種IO多路複用模型在不一樣的平臺有着不一樣的支持,而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)
服務端
相關文章
相關標籤/搜索