⑴ UDP堵塞怎麼解決
第一次調用 socket() 建 立套接字描述符的時候,內核就將它設置為阻塞。如果不想套接字阻塞, 就要調用函數 fcntl():
#include <unistd.h>
#include <fontl.h>
.
.
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
.
.
如果嘗試著從一個非阻塞的套接字讀信息並且沒有任何數據,它不允許阻 塞--它將返回 -1 並將 errno 設置為 EWOULDBLOCK。 但是一般說來,這種詢問不是個好主意。如果你讓你的程序在忙等狀 態查詢套接字的數據,你將浪費大量的 CPU 時間。
⑵ 阻塞、非阻塞、多路復用、同步、非同步、BIO、NIO、AIO 一文搞定
關於IO會涉及到阻塞、非阻塞、多路復用、同步、非同步、BIO、NIO、AIO等幾個知識點。知識點雖然不難但平常經常容易搞混,特此Mark下,與君共勉。
阻塞IO情況下,當用戶調用 read 後,用戶線程會被阻塞,等內核數據准備好並且數據從內核緩沖區拷貝到用戶態緩存區後 read 才會返回。可以看到是阻塞的兩個部分。
非阻塞IO發出read請求後發現數據沒准備好,會繼續往下執行,此時應用程序會不斷輪詢polling內核詢問數據是否准備好,當數據沒有準備好時,內核立即返回EWOULDBLOCK錯誤。直到數據被拷貝到應用程序緩沖區,read請求才獲取到結果。並且你要注意!這里最後一次 read 調用獲取數據的過程,是一個同步的過程,是需要等待的過程。這里的同步指的是 內核態的數據拷貝到用戶程序的緩存區這個過程 。
非阻塞情況下無可用數據時,應用程序每次輪詢內核看數據是否准備好了也耗費CPU,能否不讓它輪詢,當內核緩沖區數據准備好了,以事件通知當機制告知應用進程數據准備好了呢?應用進程在沒有收到數據准備好的事件通知信號時可以忙寫其他的工作。此時 IO多路復用 就派上用場了。
IO多路復用中文比較讓人頭大,IO多路復用的原文叫 I/O multiplexing,這里的 multiplexing 指的其實是在單個線程通過記錄跟蹤每一個Sock(I/O流)的狀態來同時管理多個I/O流. 發明它的目的是盡量多的提高伺服器的吞吐能力。實現一個線程監控多個IO請求,哪個IO有請求就把數據從內核拷貝到進程緩沖區,拷貝期間是阻塞的!現在已經可以通過採用mmap地址映射的方法,達到內存共享效果,避免真復制,提高效率。
像 select、poll、epoll 都是I/O多路復用的具體的實現。
select是第一版IO復用,提出後暴漏了很多問題。
poll 修復了 select 的很多問題。
但是poll仍然不是線程安全的, 這就意味著不管伺服器有多強悍,你也只能在一個線程裡面處理一組 I/O 流。你當然可以拿多進程來配合了,不過然後你就有了多進程的各種問題。
epoll 可以說是 I/O 多路復用最新的一個實現,epoll 修復了poll 和select絕大部分問題, 比如:
橫軸 Dead connections 是鏈接數的意思,叫這個名字只是它的測試工具叫deadcon。縱軸是每秒處理請求的數量,可看到epoll每秒處理請求的數量基本不會隨著鏈接變多而下降的。poll 和/dev/poll 就很慘了。但 epoll 有個致命的缺點是只有 linux 支持。
比如平常Nginx為何可以支持4W的QPS是因為它會使用目標平台上面最高效的I/O多路復用模型。
然後你會發現上面的提到過的操作都不是真正的非同步,因為兩個階段總要等待會兒!而真正的非同步 I/O 是內核數據准備好和數據從內核態拷貝到用戶態這兩個過程都不用等待。
很慶幸,Linux給我們准備了 aio_read 跟 aio_write 函數實現真實的非同步,當用戶發起aio_read請求後就會自動返回。內核會自動將數據從內核緩沖區拷貝到用戶進程空間,應用進程啥都不用管。
我強力推薦C++後端開發免費學習地址:C/C++Linux伺服器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂
同步跟非同步的區別在於 數據從內核空間拷貝到用戶空間是否由用戶線程完成 ,這里又分為同步阻塞跟同步非阻塞兩種。
我們以同步非阻塞為例,如下可看到,在將數據從內核拷貝到用戶空間這一過程,是由用戶線程阻塞完成的。
可發現,用戶在調用之後會立即返回,由內核完成數據的拷貝工作,並通知用戶線程,進行回調。
在Java中,我們使用socket進行網路通信,IO主要有三種模式,主要看 內核支持 哪些。
同步阻塞IO ,每個客戶端的Socket連接請求,服務端都會對應有個處理線程與之對應,對於沒有分配到處理線程的連接就會被阻塞或者拒絕。相當於是 一個連接一個線程 。
BIO特點 :
常量:
主類:
服務端監聽線程:
服務端處理線程:
客戶端:
同步非阻塞IO之NIO :伺服器端保存一個Socket連接列表,然後對這個列表進行輪詢,如果發現某個Socket埠上有數據可讀時說明讀就緒,則調用該socket連接的相應讀操作。如果發現某個 Socket埠上有數據可寫時說明寫就緒,則調用該socket連接的相應寫操作。如果某個埠的Socket連接已經中斷,則調用相應的析構方法關閉該埠。這樣能充分利用伺服器資源,效率得到了很大提高,在進行IO操作請求時候再用個線程去處理,是 一個請求一個線程 。Java中使用Selector、Channel、Buffer來實現上述效果。
每個線程中包含一個 Selector 對象,它相當於一個通道管理器,可以實現在一個線程中處理多個通道的目的,減少線程的創建數量。遠程連接對應一個channel,數據的讀寫通過buffer均在同一個 channel 中完成,並且數據的讀寫是非阻塞的。通道創建後需要注冊在 selector 中,同時需要為該通道注冊感興趣事件(客戶端連接服務端事件、服務端接收客戶端連接事件、讀事件、寫事件), selector 線程需要採用 輪訓 的方式調用 selector 的 select 函數,直到所有注冊通道中有興趣的事件發生,則返回,否則一直阻塞。而後循環處理所有就緒的感興趣事件。以上步驟解決BIO的兩個瓶頸:
下面對以下三個概念做一個簡單介紹,Java NIO由以下三個核心部分組成:
channel和buffer有好幾種類型。下面是Java NIO中的一些主要channel的實現:
正如你所看到的,這些通道涵蓋了UDP和TCP網路IO,以及文件IO。以下是Java NIO里關鍵的buffer實現:
在微服務階段,一個請求可能涉及到多個不同服務之間的跨伺服器調用,如果你想實現高性能的PRC框架來進行數據傳輸,那就可以基於Java NIO做個支持長連接、自定義協議、高並發的框架,比如Netty。Netty本身就是一個基於NIO的網路框架, 封裝了Java NIO那些復雜的底層細節,給你提供簡單好用的抽象概念來編程。比如Dubbo底層就是用的Netty。
AIO是非同步非阻塞IO,相比NIO更進一步,進程讀取數據時只負責發送跟接收指令,數據的准備工作完全由操作系統來處理。
推薦一個零聲教育C/C++後台開發的免費公開課程,個人覺得老師講得不錯,分享給大家:C/C++後台開發高級架構師,內容包括Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK等技術內容,C/C++Linux伺服器開發/後台架構師【零聲教育】-學習視頻教程-騰訊課堂 立即學習
原文:阻塞、非阻塞、多路復用、同步、非同步、BIO、NIO、AIO 一鍋端