非阻塞套接字與IO多路復用

2022-07-13 16:15:09 字數 4515 閱讀 6529

我們了解了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

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

什麼是io多路復用技術呢,簡單來說,就是我們把套接字交給作業系統去監控。

使用select函式進行io請求和同步阻塞模型沒有太大的區別,甚至還多了新增監視socket,以及呼叫select函式的額外操作,感覺效率更差。

但是,使用select以後最大的優勢是使用者可以在乙個執行緒內同時處理多個socket的io請求。使用者可以註冊多個socket,然後不斷地呼叫select讀取被啟用的socket,

即可達到在同乙個執行緒內同時處理多個io請求的目的。而在同步阻塞模型中,必須通過多執行緒的方式才能達到這個目的。

epoll是目前linux上效率最高的io多路復用技術。

epoll是惰性的事件**,惰性事件**是由使用者程序自己呼叫的,作業系統只起到通知的作用。

epoll實現併發伺服器,處理多個客戶端

import

socket

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來說,諸如套介面...