摘要:目前的常用的復用模型有三種,,。如果設置為,則將一直阻塞到有套接字滿足條件。模型是和的增強版,同一樣,文件描述符數量無限制。支持多種多路復用技術,和等。同時為文件描述符信號超時設定等事件提供了監聽回調。
“ 閱讀本文大概需要 6 分鐘。”
我們之前采用的多進程方式實現的服務器端,一次創建多個工作子進程來給客戶端提供服務。其實這種方式是存在問題的。
可以打個比方:如果我們先前創建的幾個進程承載不了目前快速發展的業務的話,是不是還得增加進程數?我們都知道系統創建進程是需要消耗大量資源的,所以這樣就會導致系統資源不足的情況。
那么有沒有一種方式可以讓一個進程同時為多個客戶端端提供服務?
接下來要講的IO復用技術就是對于上述問題的最好解答。
對于IO復用,我們可以通過一個例子來很好的理解它。(例子來自于《TCP/IP網絡編程》)
某教室有10名學生和1名老師,這些學生上課會不停的提問,所以一個老師處理不了這么多的問題。那么學校為每個學生都配一名老師,
也就是這個教室目前有10名老師。此后,只要有新的轉校生,那么就會為這個學生專門分配一個老師,因為轉校生也喜歡提問題。如果把以上例子中的學生比作客戶端,那么老師就是負責進行數據交換的服務端。則該例子可以比作是多進程的方式。
后來有一天,來了一位具有超能力的老師,這位老師回答問題非常迅速,并且可以應對所有的問題。而這位老師采用的方式是學生提問前必須先舉手,確認舉手學生后在回答問題。則現在的情況就是IO復用。
目前的常用的IO復用模型有三種:select,poll,epoll。
select模型:
說的通俗一點就是各個客戶端連接的文件描述符也就是套接字,都被放到了一個集合中,調用select函數之后會一直監視這些文件描述符中有哪些可讀,如果有可讀的描述符那么我們的工作進程就去讀取資源。PHP 中有內置的函數來完成 select 系統調用。
函數原型:
int socket_select (array &$read ,array &$write ,array &$except ,int $tv_sec [,int $tv_usec= 0 ])
作用說明:用于確定一個或多個套接字的狀態,對每一個套接字,調用者可查詢它的可讀性、可寫性及錯誤狀態信息
參數說明:
read: 指向一組等待可讀性檢查的套接字
write: 指向一組等待可寫性檢查的套接字
except: 指向一組等待錯誤檢查的套接字
tv_sec: 用來設置 select() 的等待時間,秒
tv_usec: 用來設置 select() 的等待時間,微妙
這里注意一下,如果 tv_sec 設置為0,則 socket_select 立即返回,也就是非阻塞的。如果 tv_sec 設置為 null ,則 socket_select 將一直阻塞到有套接字滿足條件。
下面通過代碼代碼來簡單舉例:
poll模型:
poll 和 select 的實現非常類似,本質上的區別就是存放 fd 集合的數據結構不一樣。select 在一個進程內可以維持最多 1024 個連接,poll 在此基礎上做了加強,可以維持任意數量的連接。
但 select 和 poll 方式有一個很大的問題就是,我們不難看出來 select 是通過輪訓的方式來查找是否可讀或者可寫,打個比方,如果同時有100萬個連接都沒有斷開,而只有一個客戶端發送了數據,所以這里它還是需要循環這么多次,造成資源浪費。
所以后來出現了 epoll 系統調用。
epoll模型:
epoll 是 select 和 poll 的增強版,epoll 同 poll 一樣,文件描述符數量無限制。
epoll是基于內核的反射機制,在有活躍的 socket 時,系統會調用我們提前設置的回調函數。而 poll 和 select 都是遍歷。
但是也并不是所有情況下 epoll 都比 select/poll 好,比如在如下場景:
在大多數客戶端都很活躍的情況下,系統會把所有的回調函數都喚醒,所以會導致負載較高。既然要處理這么多的連接,那倒不如 select 遍歷簡單有效。
在 PHP 中我們可以使用 libevet 拓展來實現 epoll。
libevent 是一個用C語言寫的,基于事件驅動的高性能網絡庫。支持多種 I/O 多路復用技術,epoll、 poll、 dev/poll、 select 和 kqueue 等。 libevent 同時為文件描述符、信號、超時設定等事件提供了監聽回調。所以這種編程方式也可以說是事件編程。
先放代碼體驗一番:
服務端:
客戶端:
先說簡單的客戶端,客戶端的主要作用也就是像服務端發送了兩句話。第一句是 hello world!,然后等待兩秒之后再次發送 send again!
并且每次發送之后都將接收到服務端返回的字節數。
講解服務端之前先了解一下關于時間循環的一些函數:
event_base_new 創建一個事件庫(只需創建一次)
event_new 創建事件
event_set 為創建的事件設置要監聽文件描述符fd,以及事件類型、回調函數
event_base_set 將創建的事件與事件庫關聯
event_add 將設置好的事件加入事件監聽器
event_base_loop 開啟事件循環
還有 event_set 的幾個參數:
EV_TIMEOUT: 超時
EV_READ: 只要網絡緩沖中還有數據,回調函數就會被觸發
EV_WRITE: 只要塞給網絡緩沖的數據被寫完,回調函數就會被觸發
EV_SIGNAL: POSIX信號量
EV_PERSIST: 不指定這個屬性的話,回調函數被觸發后事件會被刪除
服務端的三個函數:
read_cb() 接受數據,發送數據
error_cb() 錯誤處理
accept_cb() 受理請求并且把新的文件描述符加入事件庫,同時注冊 read_cb 回調
整個服務端的主要流程如下:
1.創建事件庫
2.設置事件回調
3.綁定事件
4.開始事件循環
5.如有符合條件的文件描述符則系統開始調用我們提前設定好的處理函數
至于詳細的流程我就不分析了,大致流程應該都能理解,接下來就靠自己鞏固了。一定要自己動手實踐才行。
當然本文也只是起到拋磚引玉而已,以上有問題的地方歡迎指出。由于作者沒有開通留言功能,所以歡迎后臺直接回復哦。( 阿毛的Coding之路 )
本人會持續分享一些關于編程以及編程自學相關的文章(不限于PHP),記錄自己的自學編程之路。同時希望自己的分享能夠幫助一些對編程感興趣以及正在編程道路上的朋友。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30891.html
摘要:前面幾講手撕了網關服務器回顯服務器服務的代碼,但是這幾個一次只能監聽一個文件描述符,因此性能非常原始低下。復用能使服務器同時監聽多個文件描述符,是服務器性能提升的關鍵。表示要操作的文件描述符,指定操作類型,指定事件。 ?本系列文章導航: 手把手寫C++服務器(0):專欄文章-匯總導航【更...
摘要:基本概念多路復用是指內核一旦發現進程指定的一個或者多個條件準備讀取它就通知該進程多路復用適用如下場合當客戶處理多個描述字時一般是交互式輸入和網絡套接口必須使用復用當一個客戶同時處理多個套接口時而這種情況是可能的但很少出現如果一個服務器既要處 基本概念 IO多路復用是指內核一旦發現進程指定的一個或者多個IO條件準備讀取, 它就通知該進程. IO多路復用適用如下場合: (1)當客...
閱讀 2757·2021-11-22 14:45
閱讀 903·2021-10-15 09:41
閱讀 1065·2021-09-27 13:35
閱讀 3679·2021-09-09 11:56
閱讀 2632·2019-08-30 13:03
閱讀 3196·2019-08-29 16:32
閱讀 3303·2019-08-26 13:49
閱讀 770·2019-08-26 10:35