PythonNET網絡編程3

PythonNET網絡編程3

IO

IO   input   output
在內存中存在數據交換的操作都可以認爲是IO操作
和終端交互 : input   print
和磁盤交互 : read  write
和網絡交互 : recv   send
  • IO密集型程序:在程序執行過程中存在大量IO操作,而cpu運算操作較少,消耗cpu較少,運行效率較低

  • 計算密集型程序(CUP密集型程序):在程序執行中CPU運算較多,IO操作相對較少,消耗CPU大,運行速度快

  • IO分類
    • 阻塞IO
    • 非阻塞IO
    • IO多路複用

阻塞IO

  • 阻塞IO是IO的默認形態,是效率較低的一種IO情形。
  • 阻塞情況
    • 因爲某種條件沒有達成造成的阻塞
      • e.g. accept input recv
    • 處理IO數據傳輸時間較長形成的阻塞
      • e.g. 網絡傳輸過程,文件讀寫過程

非阻塞IO

  • 通過修改IO事件的屬性,使其變爲非阻塞狀態(讓一些條件阻塞函數不再阻塞)
    • 非阻塞IO往往和循環判斷一起使用
      s.setblocking(False)
      將套接字設置爲非阻塞狀態
# 設置非阻塞
from socket import * 
from time import sleep,ctime 

s = socket()
s.bind(('127.0.0.1',9999))
s.listen(3)

#將套接字設置爲非阻塞
s.setblocking(False)

while True:
    print("Waiting for connect...")
    try:
        c,addr = s.accept()
    except BlockingIOError:
        sleep(2)
        print(ctime())
        continue 
    else:
        print("Connected from",addr)
        while True:
            data = c.recv(1024).decode()
            if not data:
                break
            print(data)
            c.send(ctime().encode())
        c.close()
s.close()
  • 超時檢測
    • 將原本阻塞的函數設置一個最長阻塞時間,如果時間內條件達成則正常運行,如果仍然阻塞則視爲超時,繼續向下運行或產生異常

        s.settimeout(sec)  
        設置套接字的超時時間
# 設置超時時間
from socket import * 
from time import sleep,ctime 

s = socket()
s.bind(('127.0.0.1',8888))
s.listen(3)

#將套接字設置超時時間
s.settimeout(5)

while True:
    print("Waiting for connect...")
    try:
        c,addr = s.accept()
    except timeout:
        print(ctime())
        continue 
    else:
        print("Connect from",addr)
        while True:
            data = c.recv(1024).decode()
            if not data:
                break
            print(data)
            c.send(ctime().encode())
        c.close()
s.close()

IO多路複用

  • 定義:同時監控多個IO事件,當哪個IO事件準備就緒就執行哪個IO事件,以此形成可用同時操作多個IO的併發行爲,避免一個IO阻塞,造成所有IO都無法執行。
  • IO準備就緒:是一種IO必然要發生的臨界狀態

  • IO多路複用的編程實現
    1. 將IO設置爲關注IO
    2. 將關注IO提交給內核監測
    3. 處理內核給我們反饋的準備就緒的IO
  • 具體方案:
    select --> linux unix windows
    poll --> linux unix
    epoll --> linux unix

select

import select
rs,ws,xs = select(rlist, wlist, xlist[, timeout])

  • 功能:監控IO事件,阻塞等待IO事件發生
  • 參數:
    • rlist..列表..存放我們監控等待處理的IO事件
    • wlist..列表..存放我們要主動操作的IO事件
    • xlist..列表..我們要關注出錯處理的IO事件
    • timeout..超時時間
  • 返回值:
    • rs..列表..rlist中準備就緒的IO
    • ws..列表..wlist中準備就緒的IO
    • xs..列表..xlist中準備就緒的IO
  • 注意:
    1. wlist中如果有IO事件則select立即回返回爲ws
    2. 在處理IO過程中不要處理一個客戶端長期佔有服務端使服務端無法運行到select的情況
    3. IO多路複用佔用計算機資源少,io效率高
from select import select 
from socket import *

#創建套接字作爲我們關注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',9999))
s.listen(5)

rlist = [s]
wlist = []
xlist = [s]

while True:
    #提交監測我們關注的IO等待IO發生
    rs,ws,xs = select(rlist,wlist,xlist)
    for r in rs:
        if r is s:
            c,addr = r.accept()
            print("Connect from",addr)
            rlist.append(c) #添加到關注列表
        else:
            data = r.recv(1024)
            if not data:
                rlist.remove(r)
                r.close()
            else:
                print(data.decode())
                # r.send(b'Receive your message')
                #將客戶端套接字放入wlist列表
                wlist.append(r)
                
    for w in ws:
        w.send(b'Receive your message')
        wlist.remove(w)

    for x in xs:
        if x is s:
            s.close()

位運算

  • 整數按照二進制位進行運算
    • &:按位與
    • |:按位或
    • ^:按位異或
    • <<:左移
    • >>:右移

      11 1011

      14 1110

      14 & 11 : 1010 一0則0

      14 | 11 : 1111 一1則1

      14 ^ 11 : 0101 相同爲0不同爲1

      11 << 2 : 101100 右側補零
      14 >> 2 : 11

poll

  1. 創建poll對象
    p = select.poll()
  2. 添加註冊事件
    p.register(s, POLLIN | POLLERR)

     POLLIN   POLLOUT  POLLERR  POLLHUP  POLLNVAL
     rlist    wlist    xlist    斷開     無效數據
    
     p.unregister(s) 從關注事件中移除
  3. 阻塞等待IO發生
    events = p.poll()
    • 功能:阻塞等待IO發生
    • 返回值:events 是一個列表,列表中給每一個元素都是一個元組,代表一個發生的IO事件

        [(fileno, event),(),()....]
        就緒IO的文件描述符, 具體就緒事件
    • 需要通過文件描述符(fileno)找到對應的IO對象
      {s.fileno() : s}

  4. 處理具體的IO

from socket import * 
from select import *

#創建套接字作爲我們關注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',9999))
s.listen(5)

#創建poll對象
p = poll()

#fileno ---> IO對象的字典
fdmap = {s.fileno():s}

#註冊關注的IO
p.register(s,POLLIN | POLLERR)

while True:
    #進行IO監控
    events = p.poll()
    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #添加新的關注事件
            p.register(c,POLLIN | POLLHUP)
            fdmap[c.fileno()] = c
        elif event & POLLIN:    # 若是真,POLLIN就緒
            data = fdmap[fd].recv(1024)
            if not data:
                #客戶端退出,從關注事件移除
                p.unregister(fd)
                fdmap[fd].close()
                del fdmap[fd]
            else:
                print(data.decode())
                fdmap[fd].send(b'Receive')

epoll

  • 使用方法:基本與poll方法相同
    • 將生產對象 poll() 改爲epoll()
    • 將所有poll對象事件改爲epoll對象事件
  • 區別 :
    • epoll 的效率要比 poll和select 高
    • epoll 的事件觸發方式更多
from socket import * 
from select import *

#創建套接字作爲我們關注的IO
s = socket()
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(('0.0.0.0',9999))
s.listen(5)

#創建epoll對象
p = epoll()

#fileno ---> IO對象的字典
fdmap = {s.fileno():s}

#註冊關注的IO
p.register(s,EPOLLIN | EPOLLERR)

while True:
    #進行IO監控
    events = p.poll()
    for fd,event in events:
        if fd == s.fileno():
            c,addr = fdmap[fd].accept()
            print("Connect from",addr)
            #添加新的關注事件
            p.register(c,EPOLLIN | EPOLLHUP)
            fdmap[c.fileno()] = c
        elif event & EPOLLIN:    
            data = fdmap[fd].recv(1024)
            if not data:
                #客戶端退出,從關注事件移除
                p.unregister(fd)
                fdmap[fd].close()
                del fdmap[fd]
            else:
                print(data.decode())
                fdmap[fd].send(b'Receive')
posted @ 2019-04-04 14:06 黑洞頻率 閱讀(...) 評論(...) 編輯 收藏
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章