python非阻塞输入_python_非阻塞套接字及I/O流
首先,我們要明確2個問題:
普通套接字實現的服務端有什么缺陷嗎?
有,一次只能服務一個客戶端!
這種缺陷是如何造成的?
accept阻塞:當沒有套接字連接請求過來的時候會一直等待著
recv阻塞:當連接的這個客戶端沒有發數據過來的時候,也會一直等待著
importsocket
server=socket.socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)print("執行到這, 上面沒問題了")whileTrue:
conn, addr= server.accept() #阻塞
print(conn, addr)print("{}連接".format(addr))whileTrue:
data= conn.recv(1024) #阻塞
print(data)if notdata:break
當前I/O流
那么非阻塞套接字和普通套接字的區別?
非阻塞套接字在accept或recv的時候不會發生阻塞,要么成功,要么失敗拋出BlockingIOError異常
非阻塞IO模型
非阻塞套接字實現并發
并發是什么?
在一個時間段,完成某件事,就是并發
對立的概念,什么是并行?
同時發生,不管有幾件事,同時進行,就是并行
非阻塞套接字如何實現并發服務端?
配合try語句,將代碼順序重排,避開阻塞
實現并發服務多個客戶端 !
那么現在用非阻塞套接字完善上一章博客的代碼:
服務端:
importsocketimporttime#并發操作
server= socket.socket() #創建一個socket
server.setblocking(False) # 設置成非阻塞
server.bind(('0.0.0.0', 8888))
server.listen()print("執行到這, 上面沒問題了")
all_connction= [] #用來存放和連接客戶端通信的套接字
whileTrue:#處理用戶的連接
try:
conn, addr= server.accept() #阻塞
conn.setblocking(False) # 設置成非阻塞
print(conn, addr)print("{}連接".format(addr))
all_connction.append(conn)exceptBlockingIOError as e:pass
#處理已經連接的客戶的消息
time.sleep(1)
new_li=all_connction.copy()for conn innew_li:try:
data= conn.recv(1024) #阻塞
if data == b'':
all_connction.remove(conn)
conn.close()else:print("接收到的數據:", data.decode())
conn.send(data)exceptBlockingIOError as e:passserver.close()
客戶端:
#客戶端Linux、window系統下:輸入命令通過服務端返回
importsocket#聲明協議類型,同時生成socket連接對象
client =socket.socket()#鏈接地址和端口,元組(本地,端口)
client.connect(('127.0.0.1', 8888))#使用input循環向服務端發送請求
whileTrue:
msg= input("-->>:").strip()if len(msg) ==0:continue
#發送數據 b將字符串轉為bys類型
client.send(msg.encode("utf-8"))#接收服務器端的返回,需要聲明收多少,默認1024字節
id = 1024data=client.recv(id).decode()#打印data是recv的data
print("recv: %s" %data)#關閉接口
client.close()
IO多路復用
IO多路復用也是阻塞IO, 只是阻塞的方法是select/poll/epoll, 好處就是單個進程可以處理多個socket
用select,poll,epoll監聽多個io對象,當io對象有變化(有數據)的時候,則立即通知相應程序進行讀或者寫操作。
但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫時間就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的
因為阻塞I/O只能阻塞一個I/O操作,而I/O復用模型能夠阻塞多個I/O操作, 所以才叫做多路復用
非阻塞套接字實現的服務端還有什么不完美的地方嗎?
關鍵一: 任何操作都是要花CPU資源的!
關鍵二: 如果數據還沒有到達。那么accept, recv操作都是在做無用功!
關鍵三: 對異常BlockIOError的處理也是在做無用功!
總結:不完美的CPU利用率
I/O多路復用模型
epoll? 目前Linux上效率最高的IO多路復用技術!
epoll 基于惰性的事件回調機制
惰性的事件回調是由用戶自己調用的,操作系統只起到通知的作用
使用步驟
? ? 導入IO多路復用選擇器
注冊事件和回調
查詢
? ? ?回調
應用實例:
import selectors #提供IO多路技術給我們使用的
importsocketimporttime
server= socket.socket() #創建一個套接字
server.bind(('0.0.0.0', 8888))
server.listen()
epoll= selectors.EpollSelector() #生成一個epoll
defnewFunc(conn):
data= conn.recv(1024)if data == b'':
epoll.unregister(conn)#取消注冊
conn.colse()else:print("收到的數據:",data.decode())
conn.send(data)deffunc(ser):
conn, addr=ser.accept()print("處理了連接")#查看是否有數據
epoll.register(conn, selectors.EVENT_READ, newFunc)
time.sleep(1)
epoll.register(server, selectors.EVENT_READ, func)#注冊 給他3個參數 1.想看什么,看的是server 2.有沒有數據連接(EVENT_READ可讀,EVENT_WRITE可寫) 3.回調函數
whileTrue:
events= epoll.select() #查詢
for key, mask inevents:
callback= key.data #函數的名字
sock = key.fileobj #套接字
callback(sock)#函數調用
importsocket
client=socket.socket()
client.connect(('127.0.0.1', 8888))whileTrue:
data= input("請輸入要發送的數據:")
client.send(data.encode())print("接收到的數據:", client.recv(1024).decode())
下面是一些理論的東西,大家有時間可以讀一遍。
## 什么是IO操作
? IO在計算機中指Input/Output,也就是輸入和輸出 。由于程序和運行時數據是放在內存中的,由CPU來執行的,涉及到數據交換的地方,通常是磁盤、網絡等,就需要IO接口。
? 比如你打開瀏覽器,訪問百度,瀏覽器需要通過網絡獲取百度的網頁數據。瀏覽器首先發送數據給百度的服務器,告訴它我要訪問它,這個過程是往外發數據,就做Output。然后百度服務器在把網頁數據發過來。這個過程是從外面接收數據,就做input
? 所以,通常,程序完成IO操作會有Input和Output 這兩個過程, 但是也可能只有一個,比如打開一個文件。就只是從磁盤讀取文件到內存,就只有Input操作 ,反過來,向文件中寫入數據,就只是一個Output操作。
## 1 流的概念
? IO編程中,Stream(流)是一個很重要的概念,可以把流想象成一個水管,數據就是水管里的水,但是只能單向流動。Input Stream就是數據從外面(磁盤、網絡)流進內存,Output Stream就是數據從內存流到外面去。對于瀏覽網頁來說,瀏覽器和百度服務器之間至少需要建立兩根水管,才可以既能發數據,又能收數據。
? 由于CPU和內存的速度遠遠高于外設的速度,所以,在IO編程中,就存在速度嚴重不匹配的問題。 可能存在這樣的情況:讀取數據的時候,流中還沒有數據;寫入數據的時候,流中數據已經滿了,沒有空間寫入了。
? 舉個例子,socket通信, 通過recv讀取另一方發過來的數據,但是對方還沒把數據準備好發過來。此時有兩種處理辦法:
- 阻塞,等待數據準備好了,再讀取出來返回;
- 非阻塞,通過輪詢的方式,查詢是否有數據可以讀取,直到把數據讀取返回。
## 2. 同步,異步,阻塞, 非阻塞的概念
在IO操作過程中,可能會涉及到同步(synchronous)、異步(asynchronous)、阻塞(blocking)、非阻塞(non-blocking)、IO多路復用(IO multiplexing)等概念。他們之間的區別是什么呢?
以socket為例子,在socket通信過程中,涉及到兩個對象:
1. 調用這個IO的進程(process)或線程(thread)
2. 操作系統內核(kernel)
比如服務端調用recv來接收客戶端的數據,會涉及兩個過程:
1. 等待數據準備好(Waiting for the data to be ready),也就是客戶端要通過網絡把數據發給服務端;
2. 客戶端把數據發送過來,首先會被操作系統內核接收到,程序里面需要使用這個數據,要將數據從內核中拷貝到進程中( Copying the data from the kernel to the process))
根據這兩個階段中,不同階段是否發生阻塞,將產生不同的效果。
### 阻塞 VS 非阻塞
阻塞IO:
- 在1、2階段都發生阻塞;
- 調用阻塞IO會一直block住進程,直到操作完成
非阻塞IO:
- 在第1階段沒有阻塞,在第2階段發生阻塞;
- 當用戶進程發出IO請求時, 如果內核中的數據還沒由準備好,那么它并不會block用戶進程,而是立即返回一個錯誤, 在程序看來,它發起一個請求后,并不需要等待,而是馬上就得到一個結果。
- 非阻塞IO需要不斷輪詢,查看數據是否已經準備好了;
阻塞與非阻塞可以簡單理解為調用一個IO操作能不能立即得到返回應答,如果不能立即獲得返回,需要等待,那就阻塞了;否則就可以理解為非阻塞 。
### **同步VS異步**
同步與異步是針對應用程序與內核的交互而言的
同步:第二步數據從內核緩存寫入用戶緩存一定是由用戶線程自行讀取數據,處理數據。
異步:第二步數據是內核寫入的,并放在了用戶線程指定的緩存區,寫入完畢后通知用戶線程。
?同步和異步針對應用程序來,關注的是程序中間的協作關系;阻塞與非阻塞更關注的是單個進程的執行狀態。
?同步有阻塞和非阻塞之分,異步沒有,它一定是非阻塞的。
?阻塞、非阻塞、多路IO復用,都是同步IO,異步必定是非阻塞的,所以不存在異步阻塞和異步非阻塞的說法。真正的異步IO需要CPU的深度參與。換句話說,只有用戶線程在操作IO的時候根本不去考慮IO的執行全部都交給CPU去完成,而自己只等待一個完成信號的時候,才是真正的異步IO。所以,拉一個子線程去輪詢、去死循環,或者使用select、poll、epool,都不是異步。
?同步:執行一個操作之后,進程觸發IO操作并等待(也就是我們說的阻塞)或者輪詢的去查看IO操作(也就是我們說的非阻塞)是否完成,等待結果,然后才繼續執行后續的操作。
?異步:執行一個操作后,可以去執行其他的操作,然后等待通知再回來執行剛才沒執行完的操作。
?阻塞:進程給CPU傳達一個任務之后,一直等待CPU處理完成,然后才執行后面的操作。
?非阻塞:進程給CPU傳達任務后,繼續處理后續的操作,隔斷時間再來詢問之前的操作是否完成。這樣的過程其實也叫輪詢。
## 3. IO多路復用
?I/O多路復用也是阻塞IO,只是阻塞的方法是select/poll/epoll ,好處就是單個進程可以處理多個socket
用select, poll, epoll監聽多個io對象,當io對象有變化(有數據)的時候,則立即通知相應程序進行讀或寫操作。但select,poll,epoll本質上都是同步I/O,因為他們都需要在讀寫事件就緒后自己負責進行讀寫,也就是說這個讀寫過程是阻塞的
因為阻塞I/O只能阻塞一個I/O操作,而I/O復用模型能夠阻塞多個I/O操作,所以才叫做多路復用。
到這里整理完畢。
博客鏈接:https://www.cnblogs.com/lixy-88428977
聲明:本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。如果您認為還不錯,歡迎轉載。轉載與引用請注明作者及出處。
總結
以上是生活随笔為你收集整理的python非阻塞输入_python_非阻塞套接字及I/O流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python希尔排序的优缺点_Pytho
- 下一篇: websocket python爬虫_p