我們了解了socket之後已經知道,普通套接字實現的服務端的缺陷:一次只能服務乙個客戶端!
並且,為了使乙個客戶端能夠不斷收發訊息,我們還要使用while迴圈來輪詢,這極大地降低了我們的效率
accept阻塞!
在沒有新的套接字來之前,不能處理已經建立連線的套接字的請求
recv 阻塞!
在沒有接受到客戶端請求資料之前,不能與其他客戶端建立連線
可以用非阻塞介面來嘗試解決這個問題!
阻塞io模型
阻塞io(blocking io)的特點:就是在io執行的兩個階段(等待資料和拷貝資料兩個階段)都被block了。
什麼是阻塞呢?想象這種情形:你要去車站接朋友,到了車站之後發現車還沒到站,你現在有兩種選擇:
繼續在等著,直到朋友的車到站。
先去幹點別的,然後時不時地聯絡你的朋友詢問車是否到站,朋友說到了,你再去車站
很明顯,大多數人都會選擇第二種方案。
而在計算機世界,這兩種情形就對應阻塞和非阻塞忙輪詢。
非阻塞io模型
非阻塞套接字和阻塞套接字的區別:
把套接字設定為非阻塞之後,如果沒有得到想要的資料,就會丟擲乙個blockingioerror的異常。我們可以通過捕獲處理這個異常,讓程式正常完成
非阻塞式io中,使用者程序其實是需要不斷的主動詢問kernel資料準備好了沒有
非阻塞如何利用
程式設計正規化:
服務端
importsocket客戶端conn_addr = ('127.0.0.1', 9999)
conn_list = # 連線列表
server = socket.socket() # 開啟socket
server.setblocking(false) # 設定為非阻塞
server.bind(conn_addr) # 繫結ip和埠到套接字
server.listen(5) # 監聽,5表示最大掛起數
print('start listen')
whiletrue:
try:
conn,addr = server.accept() #等待客戶端連線,沒有就丟擲blockingioerror
conn.setblocking(false)
print('{}已連線'.format(addr))
exceptblockingioerror:
passconn_list = [x for x inconn_list]
for conn_socket in conn_list:#對已連線的套接字進行輪詢
try:
data = conn_socket.recv(1024) #如有客戶端傳送訊息,則列印並返回
exceptblockingioerror:
pass
else: #else在不報錯的時候才執行
if data: #判斷客戶端發過來的是不是空
print(data.decode())
conn_socket.send(data)
else: #若為空,表示客戶端已斷開
conn_socket.close()
conn_list.remove(conn_socket)
print('客戶端數目:{}'.format(len(conn_list)))
importsocket非阻塞io模型優點:實現了同時服務多個客戶端,能夠在等待任務完成的時間裡幹其他活了(包括提交其他任務,也就是 「後台」 可以有多個任務在「」同時「」執行)。client =socket.socket()
client.connect(('127.0.0.1',9999))
whiletrue:
data = input('>>>>>')
if data == 'q': #按q退出
breakclient.send(data.encode())
response = client.recv(1024)
print(response.decode())
但是非阻塞io模型絕不被推薦
非阻塞io模型缺點:
如何解決:多路復用io
什麼是io多路復用技術呢,簡單來說,就是我們把套接字交給作業系統去監控。
使用select函式進行io請求和同步阻塞模型沒有太大的區別,甚至還多了新增監視socket,以及呼叫select函式的額外操作,感覺效率更差。
但是,使用select以後最大的優勢是使用者可以在乙個執行緒內同時處理多個socket的io請求。使用者可以註冊多個socket,然後不斷地呼叫select讀取被啟用的socket,
即可達到在同乙個執行緒內同時處理多個io請求的目的。而在同步阻塞模型中,必須通過多執行緒的方式才能達到這個目的。
epoll是目前linux上效率最高的io多路復用技術。
epoll是惰性的事件**,惰性事件**是由使用者程序自己呼叫的,作業系統只起到通知的作用。
epoll實現併發伺服器,處理多個客戶端
importsocket
import
selectors
#註冊乙個epllo事件引數
#1. 需要作業系統監控的套接字
#2.事件(可讀還是可寫)
#3.**函式
defrecv_data(conn):
data = conn.recv(1024)
ifdata:
print('
接收的資料是:%s
' %data.decode())
conn.send(data)
else
:
print('
斷開連線
',conn)
e_poll.unregister(conn)
conn.close()
defaccept_conn(p_server):
conn, addr =p_server.accept()
print('
connected by
', addr)
#也要註冊乙個事件
e_poll.register(conn,selectors.event_read,recv_data)
conn_addr = ('
127.0.0.1
', 9999)
server =socket.socket()
server.bind(conn_addr)
server.listen(6)
#生成乙個epllo選擇器例項 i/o多路復用,監控多個socket連線
e_poll = selectors.defaultselector() #
linux是epoll,windows是select
e_poll.register(server, selectors.event_read, accept_conn)
#事件迴圈
while
true:
#事件迴圈不斷地呼叫select獲取發生變化的socket
events =e_poll.select()
for key, mask in
events:
call_back = key.data #
key.data就是**函式
call_back(key.fileobj) #
key.fileobj是套接字
IO多路復用 非阻塞式IO
目錄 三 多路復用實現 伺服器核心要做的事情是等資料到了我再做事情。伺服器的執行緒處理時間裡面只需要包含資料到了後續部分。這樣最大限度的利用了珍貴的伺服器執行緒資源,並且由於執行緒的處理時間中沒有包含等待資料的時間,可以使得執行緒快速釋放。現實生活中的例子,餐廳,阻塞io類似於每來乙個客人我馬上分配...
非同步 非阻塞和IO多路復用總結
nginx是併發處理框架的代表者,很多後台業務都會放在nginx容器中執行,以實現高吞吐,而nginx能夠支援高併發也是由於使用了非同步非阻塞處理模型,本文將用通俗的話講解非同步 同步 阻塞 非阻塞的區別,以及io多路復用。一 同步和非同步 同步與非同步的重點是在訊息通知的方式上,也就是呼叫後結果通...
I O多路復用
一 五種i o模型 1 阻塞i o模型 最流行的i o模型是阻塞i o模型,預設情形下,所有套介面都是阻塞的。我們以資料報套介面為例來講解此模型 我們使用udp而不是tcp作為例子的原因在於就udp而言,資料準備好讀取的概念比較簡單 要麼整個資料報已經收到,要麼還沒有。然而對於tcp來說,諸如套介面...