一:IOCP和Epoll之間的異同。
異:
1:IOCP是WINDOWS系統下使用。Epoll是Linux系統下使用。
2:IOCP是IO操做完畢以後,經過Get函數得到一個完成的事件通知。
Epoll是當你但願進行一個IO操做時,向Epoll查詢是否可讀或者可寫,若處於可讀或可寫狀態後,Epoll會經過epoll_wait進行通知。
3:IOCP封裝了異步的消息事件的通知機制,同時封裝了部分IO操做。但Epoll僅僅封裝了一個異步事件的通知機制,並不負責IO讀寫操做。Epoll保持了事件通知和IO操做間的獨立性,更加簡單靈活。
4: 基於上面的描述,咱們能夠知道Epoll不負責IO操做,因此它只告訴你當前可讀可寫了,而且將協議讀寫緩衝填充,由用戶去讀寫控制,此時咱們能夠作出額 外的許多操做。IOCP則直接將IO通道里的讀寫操做都作完了才通知用戶,當IO通道里發生了堵塞等情況咱們是沒法控制的。html
同:
1:它們都是異步的事件驅動的網絡模型。
2:它們均可以向底層進行指針數據傳遞,當返回事件時,除可通知事件類型外,還能夠通知事件相關數據。算法
二:描述一下IOCP:
扯遠點。首先傳統服務器的網絡IO流程以下:
接到一個客戶端鏈接->建立一個線程負責這個鏈接的IO操做->持續對新線程進行數據處理->所有數據處理完畢->終止線程。
可是這樣的設計代價是:
1:每一個鏈接建立一個線程,將致使過多的線程。
2:維護線程所消耗的堆棧內存過大。
3:操做系統建立和銷燬線程過大。
4:線程之間切換的上下文代價過大。
此時咱們能夠考慮使用線程池解決其中3和4的問題。這種傳統的服務器網絡結構稱之爲會話模型。
後來咱們爲防止大量線程的維護,建立了I/O模型,它被但願要求能夠:
1:容許一個線程在不一樣時刻給多個客戶端進行服務。
2:容許一個客戶端在不一樣時間被多個線程服務。
這樣作的話,咱們的線程則會大幅度減小,這就要求如下兩點:
1:客戶端狀態的分離,以前會話模式咱們能夠經過線程狀態得知客戶端狀態,但如今客戶端狀態要經過其餘方式獲取。
2:I/O請求的分離。一個線程再也不服務於一個客戶端會話,則要求客戶端對這個線程提交I/O處理請求。
那麼就產生了這樣一個模式,分爲兩部分:
1:會話狀態管理模塊。它負責接收到一個客戶端鏈接,就建立一個會話狀態。
2:當會話狀態發生改變,例如斷掉鏈接,接收到網絡消息,就發送一個I/O請求給 I/O工做模塊進行處理。
3:I/O工做模塊接收到一個I/O請求後,從線程池裏喚醒一個工做線程,讓該工做線程處理這個I/O請求,處理完畢後,該工做線程繼續掛起。
上面的作法,則將網絡鏈接 和I/O工做線程分離爲兩個部分,相互通信僅依靠 I/O請求。
此時可知有如下一些建議:
1:在進行I/O請求處理的工做線程是被喚醒的工做線程,一個CPU對應一個的話,能夠最大化利用CPU。因此 活躍線程的個數 建議等於 硬件CPU個數。
2:工做線程咱們開始建立了線程池,免除建立和銷燬線程的代價。由於線程是對I/O進行操做的,且一一對應,那麼當I/O所有並行時,工做線程必須知足I/O並行操做需求,因此 線程池內最大工做線程個數 建議大於或者等於 I/O並行個數。
3:可是咱們可知CPU個數又限制了活躍的線程個數,那麼線程池過大意義很低,因此按常規建議 線程池大小 等於 CPU個數*2 左右爲佳。例如,8核服務器建議建立16個工做線程的線程池。
上面描述的依然是I/O模型並不是IOCP,那麼IOCP是什麼呢,全稱 IO完成端口。
它是一種WIN32的網絡I/O模型,既包括了網絡鏈接部分,也負責了部分的I/O操做功能,用於方便咱們控制有併發性的網絡I/O操做。它有以下特色:
1:它是一個WIN32內核對象,因此沒法運行於Linux.
2:它本身負責維護了工做線程池,同時也負責了I/O通道的內存池。
3:它本身實現了線程的管理以及I/O請求通知,最小化的作到了線程的上下文切換。
4:它本身實現了線程的優化調度,提升了CPU和內存緩衝的使用率。
使用IOCP的基本步驟很簡單:
1:建立IOCP對象,由它負責管理多個Socket和I/O請求。CreateIoCompletionPort須要將IOCP對象和IOCP句柄綁定。
2:建立一個工做線程池,以便Socket發送I/O請求給IOCP對象後,由這些工做線程進行I/O操做。注意,建立這些線程的時候,將這些線程綁定到IOCP上。
3:建立一個監聽的socket。
4:輪詢,當接收到了新的鏈接後,將socket和完成端口進行關聯而且投遞給IOCP一個I/O請求。注意:將Socket和IOCP進行關聯的函數和建立IOCP的函數同樣,都是CreateIoCompletionPort,不過注意傳參必然是不一樣的。
5:由於是異步的,咱們能夠去作其餘,等待IOCP將I/O操做完成會回饋咱們一個消息,咱們再進行處理。
其中須要知道的是:I/O請求被放在一個I/O請求隊列裏面,對,是隊列,LIFO機制。當一個設備處理完I/O請求後,將會將這個完成後的I/O請求丟回IOCP的I/O完成隊列。
咱們應用程序則須要在GetQueuedCompletionStatus去詢問IOCP,該I/O請求是否完成。
其中有一些特殊的事情要說明一下,咱們有時有須要人工的去投遞一些I/O請求,則須要使用PostQueuedCompletionStatus函數向IOCP投遞一個I/O請求到它的請求隊列中。數據庫
三:網絡遊戲服務器注意事項,優化措施
1:IO操做是最大的性能消耗點,注意優化餘地很大。
2:算法數據結構。排序尋路算法的優化。list,vector,hashmap的選擇。大數據尋址,不要考慮遍歷,注意考慮hash.
3:內存管理。重載new/delete,內存池,對象池的處理。
4:數據的提早準備和即時計算。
5:CPU方面的統計監視。邏輯幀計數(應當50ms之內)。
6:預分配池減小切換和調度,預處理的線程池和鏈接池等。
7:基與消息隊列的統計和信息監視框架。
8:CPU消耗排名:第一AOI同步,第二網絡發包I/O操做,第三技能/BUFF斷定計算處理,第四定時器的頻率。
9:內存泄露檢測,內存訪問越界警戒,內存碎片的回收。
10:內存消耗排名:第一玩家對象包括其物品,第二網絡數據緩衝。
11:注意32位和64位的內存容錯。
12:減小沒必要要的分包發送。
13:減小重複包和重拷貝包的代價。
14:建議分緊急包(馬上發送)和非緊急包(定時輪訓發送)。
15:帶寬消耗排名:第一移動位置同步,第二對象加載,第三登錄突發包,第四狀態機定時器消息。
16:客戶端可作部分預判斷機制,部分操做盡可能分包發送。
17:大量玩家彙集時,部分非緊急包進行丟棄。
18:注意數據庫單表內key數量。
19:活躍用戶和非活躍用戶的分割存取處理。
20:控制玩家操做對數據庫的操做頻率。
21:注意使用共享內存等方式對數據進行安全備份存儲。
22:注意安全策略,對內網進行IP檢查,對日誌進行記錄,任意兩環點內均使用加密算法會更佳。
23:實時注意對網關,數據庫等接口進行監察控制。
24:定時器應當存儲一個隊列,而非單向定位。
25:九宮格數據同步時,不須要直接進行九宮格的同步,對角色加一個AOI,基於圓方碰撞原理,拋棄沒必要要的格信息,可大幅節省。
26:客戶端作部分的預測機制,服務器檢測時注意時間戳問題。
27:按期心跳包,檢查死連接是必要的。
28:爲了實現更加負責多種類的AI,AI尋路獨立服務器設計已是必須的了。其次須要考慮的是聊天,同步。
29:服務器內網間能夠考慮使用UDP。
30:注意全部內存池,對象池等的動態擴張分配。安全
1:之內存換取CPU的理念。
2:NPC不死理念。(只會disable)
3:動態擴展理念,負載均衡理念。
4:客戶端不可信理念。
5:指針數據,消息均不可信理念。服務器