国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

淺談windows下高性能服務(wù)器程序模型IOCP

2016-04-07 16:46:36梁金晶姚宏
電腦知識(shí)與技術(shù) 2016年3期

梁金晶 姚宏

摘要:本文討論在Windows 環(huán)境下,如何開(kāi)發(fā)出一種基于TCP協(xié)議的高并發(fā)服務(wù)器程序。本文談?wù)揥indows 下套接字I/O模型,同步和異步I/O,阻塞和非阻塞模式,主要談?wù)揑OCP通訊模型的原理和開(kāi)發(fā)過(guò)程。

關(guān)鍵詞:高并發(fā);服務(wù)器端程序開(kāi)發(fā);IOCP

中圖分類(lèi)號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2016)03-0257-02

1 概述

當(dāng)前,移動(dòng)互聯(lián)網(wǎng)已成為了人們主要的獲取信息的主要渠道。在移動(dòng)互聯(lián)網(wǎng)上主要有兩種方式獲取信息。 一是通過(guò)各種瀏覽器來(lái)瀏覽網(wǎng)站內(nèi)容。二是通過(guò)手機(jī)上安裝的app來(lái)獲得來(lái)自服務(wù)器上的數(shù)據(jù),如QQ和微信。這兩種通訊方式都需要服務(wù)器端程序的支持。而衡量服務(wù)器端程序性能的一個(gè)重要指標(biāo)就是并發(fā)量,也就是同時(shí)可以接入客戶端的數(shù)量,本文討論如何在windows系統(tǒng)下最性能的服務(wù)器端程序模型完成端口模型。

2 具體內(nèi)容

首先,我們講到同步和異步兩個(gè)概念。

同步模式是比較早期的模式,在Windows套接字創(chuàng)建時(shí),默認(rèn)工作在同步模式下。同步模式就是需要服務(wù)器完成收發(fā)后才可以做下一步的操作,這樣會(huì)大大降低程序的運(yùn)行性能。所以高性能的服務(wù)器端程序多數(shù)使用異步模式。異步模式是指服務(wù)器程序不等當(dāng)前I/O是否完成,就馬上執(zhí)行下一個(gè)任務(wù),并不等待程序執(zhí)行結(jié)果或程序本身出現(xiàn)錯(cuò)誤退出。這樣就大大提高了程序性能。舉個(gè)例子來(lái)說(shuō),一個(gè)公司的物流公司的經(jīng)理給一個(gè)快遞員下達(dá)快遞任務(wù)后,等待這個(gè)快遞員送完快遞后再給第二個(gè)快遞員下達(dá)新任務(wù)。如果需要用1分鐘的時(shí)間把下單的內(nèi)容通知給快遞員,且第一個(gè)快遞員用時(shí)完成任務(wù)1小時(shí),第二個(gè)快遞員也用時(shí)1小時(shí)完成任務(wù),則一共用時(shí)2小時(shí)2分鐘,這樣的模式就是同步模式。如果經(jīng)理給第一個(gè)快遞員下完任務(wù)后不等待快遞員執(zhí)行任務(wù)的完成就直接給下一個(gè)快遞員下達(dá)第二個(gè)任務(wù),這時(shí)用時(shí)就只有2分鐘,僅僅是下單內(nèi)容通知給快遞員的時(shí)間,這樣的模式就是異步模式。很明顯,采用異步模式的程序大大提高了性能。

第二,我們講到阻塞和非阻塞兩個(gè)概念

阻塞模式就是指本線程結(jié)果返回之前,線程會(huì)被掛起該線程進(jìn)入非可執(zhí)行狀態(tài),CPU不給線程分配時(shí)間片,即線程暫停運(yùn)行,進(jìn)入待機(jī)狀態(tài)。函數(shù)只會(huì)有執(zhí)行結(jié)果或者出錯(cuò)碼生成以后才會(huì)做下一步的操作。而非阻塞模式指的是不等待執(zhí)行結(jié)果或出錯(cuò)碼的出現(xiàn)不會(huì)直接進(jìn)入睡眠狀態(tài)而是馬上執(zhí)行下一步的操作,此時(shí)線程本身還是處在激活狀態(tài)。同樣用上面經(jīng)理-快遞員的例子來(lái)說(shuō)明,阻塞模式是指經(jīng)理在下達(dá)指令后一直等待快遞員返回執(zhí)行情況。在此期間不做任何事情,直到快遞員返回喚醒經(jīng)理,再進(jìn)行下一個(gè)命令。而非阻塞模式指的是經(jīng)理下達(dá)指令后在等待返回執(zhí)行情況期間可以做其他事情,比例不停打電話給快遞員詢(xún)問(wèn)執(zhí)行情況等。

第三,我們來(lái)講到IOCP的工作原理和編寫(xiě)過(guò)程。

IOCP是Windows下高性能的可伸展的I/O模型,是最高性能的模型,與其性能相當(dāng)?shù)哪P投紱](méi)有。IOCP全稱(chēng)Input/Output Completion Port,譯為I/O完成端口。首先我們來(lái)說(shuō)為什么Windows下要使用該模型。我們知道在網(wǎng)絡(luò)上客戶端與服務(wù)器發(fā)送和接收數(shù)據(jù)(即網(wǎng)絡(luò)I/O操作)相對(duì)于CPU而言要花費(fèi)大的時(shí)間,兩者的差距可能有數(shù)幾千倍之多。顯然如果要CPU和網(wǎng)絡(luò)I/O操作采用同步模式的話即讓CPU等待網(wǎng)絡(luò)I/O完成收發(fā)數(shù)據(jù)之后再做下一步的操作,對(duì)于整個(gè)系統(tǒng)來(lái)說(shuō)是一種巨大的浪費(fèi)。所以為了提高效率,讓系統(tǒng)不等待結(jié)果而是直接做下一條操作,只是讓I/O操作完成之后再通過(guò)回調(diào)函數(shù)來(lái)通知系統(tǒng)收發(fā)執(zhí)行情況。這也是為什么叫做完成端口的原因。IOCP 其實(shí)上是一個(gè)線程池。這個(gè)線程池的核心工作是去調(diào)用I/O操作完成時(shí)的回調(diào)函數(shù)。IOCP可以想象為一個(gè)包含網(wǎng)絡(luò)通信操作即接收新客戶端的加入,發(fā)送數(shù)據(jù)到客戶端,接收客戶端上交的數(shù)據(jù)的一個(gè)信息隊(duì)列,它會(huì)把上述網(wǎng)絡(luò)操作即接收客戶端接入請(qǐng)求,發(fā)送數(shù)據(jù)和接收數(shù)據(jù)操作執(zhí)行的結(jié)果存放在這個(gè)隊(duì)列里。我們可以讀出所有的網(wǎng)絡(luò)操作的結(jié)果以及出錯(cuò)碼。

具體實(shí)現(xiàn)過(guò)程:

第一步:創(chuàng)建一個(gè)完成端口。使用HANDLE 完成端口 = CreateIoCompletionPort(參數(shù)1,參數(shù)2,參數(shù)3,參數(shù)4);參數(shù)4代表的是程序可以同時(shí)生成線程的最大數(shù)量。人們很自然地想到在一個(gè)CPU上運(yùn)行的線程數(shù)量是越多越好。但是如果線程太多的話,系統(tǒng)要在不同的線程上不斷地切換上下文,這樣不但不能加大CPU的能力,反而會(huì)降低CPU的實(shí)際使用效率。所以我在這里只是設(shè)置為零,也就是說(shuō)我們只需要讓一個(gè)CPU上運(yùn)行一個(gè)線程就好。

第二步:創(chuàng)建n個(gè)I/O操作線程。業(yè)內(nèi)一般控制n的數(shù)量為CPU數(shù)量的2倍加2,如果是4核CPU,就是4*2+2個(gè)即創(chuàng)建10個(gè)I/O操作線程。因?yàn)樵诰W(wǎng)絡(luò)上接收客戶端數(shù)據(jù)和發(fā)送數(shù)據(jù)到客戶端都是比較需要時(shí)間的事情,而在CPU上的線程操作是比較高速的,兩者的差距可能是網(wǎng)絡(luò)I/O操作用時(shí)幾千ms而CPU一個(gè)線程只是用幾個(gè)ms而已,所以只需要幾個(gè)I/O操作線程就可以滿足一臺(tái)服務(wù)器上幾萬(wàn)個(gè)客戶端的accept,send,rec執(zhí)行操作。

I/O操作線程用來(lái)處理上述講到的接收客戶端接入請(qǐng)求,發(fā)送數(shù)據(jù)和接收數(shù)據(jù)操作,也是處理我們I/O網(wǎng)絡(luò)事件的線程。那么我們就要首先獲取系統(tǒng)中CPU的數(shù)量以便確定I/O操作線程的具體數(shù)量。

使用SYSTEM_INFO, GetSystemInfo來(lái)取得Windows系統(tǒng)信息,在GetSystemInfo中的就有CPU的核心數(shù)量,然后如上所述生成的I/O操作線程為CPU核心數(shù)量的兩倍加二。

然后:把用于監(jiān)聽(tīng)的本地套接字綁定到完成端口上面并在程序設(shè)置的網(wǎng)絡(luò)端口上開(kāi)始監(jiān)聽(tīng)。綁定方法為使用CreateIoCompletionPort()函數(shù)。注意這里和創(chuàng)建完成端口的WindowsAPI完全相同。

第三步:I/O操作線程不停地取出已有的發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的結(jié)果,這些結(jié)果都存放在完成端口上,取出這些結(jié)果的函數(shù)是阻塞函數(shù),也就是說(shuō)不會(huì)被打斷,除非出錯(cuò)。

第四步:程序的主線程里只做等待客戶端的套接字連接的工作。這里可以使用Accpet()和AcceptEx()兩個(gè)函數(shù),AcceptEx是微軟專(zhuān)門(mén)在Windows操作系統(tǒng)里面提供的擴(kuò)展函數(shù),使用重疊I/O機(jī)制。在Windows里的異步I/O操作都要使用重疊I/O機(jī)制。重疊I/O機(jī)制可以簡(jiǎn)單地理解為含有這種機(jī)制的操作方法和系統(tǒng)里別的操作是異步的,不需要和別的操作有任何聯(lián)系。

擴(kuò)展函數(shù)AcceptEx()的定義

BOOL AcceptEx (

生成套接字 參數(shù)1, //生成用于本地監(jiān)聽(tīng)的套接字

生成套接字 參數(shù)2, //用于接受連接的socket,事先建好的,有客戶端連接進(jìn)來(lái)時(shí)直接把這個(gè)Socket拿給它用的那個(gè),是AcceptEx高性能的關(guān)鍵所在。一般來(lái)說(shuō),根據(jù)業(yè)務(wù)的多少來(lái)確定事先準(zhǔn)備socket的數(shù)理。

PVOID 參數(shù)3, //這個(gè)為緩沖區(qū),包含了三個(gè)信息:一是客戶端發(fā)來(lái)的第一組數(shù)據(jù),二是服務(wù)器的地址,三是客戶端的地址。

DWORD參數(shù)4, //參數(shù)3中用于存放數(shù)據(jù)的空間大小。如果為零,則Accept時(shí)將不會(huì)等待數(shù)據(jù)到來(lái),而直接返回,如果此參數(shù)不為0,那么一定得等接收到數(shù)據(jù)了才會(huì)返回。

DWORD參數(shù)5, //服務(wù)器地址信息空間的大小。

DWORD參數(shù)6, //接入客戶端地址的信息空間大小。

LPDWORD 參數(shù)7, //可不管理

LPOVERLAPPED 參數(shù)8 //重疊I/O操作所要用到的重疊結(jié)構(gòu));

第五步:這時(shí)主線程和I/O操作線程開(kāi)始分工工作,主線程處理新接入的客戶端工作,就是接入連接和安排到完成端口中去。使用accept()和acceptEx()函數(shù)接入新的客戶端,使用CreateIoCompletionPort()安排到已有的完成端口中去,同時(shí)可以在對(duì)新接入的客戶端進(jìn)行讀寫(xiě)操作。

主線程一直在等待新的客戶端的接入,這里采用的是阻塞模式,I/O操作線程則一直在做網(wǎng)絡(luò)I/O操作,I/O操作線程采用的是異步非阻塞模式,只是給操作系統(tǒng)發(fā)命令,讓操作系統(tǒng)去完成數(shù)據(jù)的收發(fā)工作,I/O操作線程這時(shí)并不等待系統(tǒng)是否完成收發(fā)工作,而是只不停的發(fā)命令,就好像上面提到的快遞員-經(jīng)理模式一樣,這里的I/O操作線程就是經(jīng)理,而做具體工作的操作系統(tǒng)就是快遞員。經(jīng)理只發(fā)命令,不監(jiān)督快遞員的具體工作,只是關(guān)心工作執(zhí)行的結(jié)果。

在操作系統(tǒng)完成收發(fā)數(shù)據(jù)后,把結(jié)果發(fā)送到完成端口上來(lái),這樣完成端口上就有了所有已有客戶端的收發(fā)數(shù)據(jù)執(zhí)行結(jié)果。

而I/O操作線程只要讀出結(jié)果就可以了,以此循環(huán)。

簡(jiǎn)單地概括完成端口模型為:

完成端口由三個(gè)主體構(gòu)成,第一主線程,第二I/O操作線程,第三操作系統(tǒng)。首先由主線程生成一個(gè)完成端口,初始化監(jiān)聽(tīng)套接字和生成兩倍CPU數(shù)量加2個(gè)的I/O操作線程,之后主線程的主要工作是接入新的客戶端。工作線程負(fù)責(zé)命令操作系統(tǒng)去接收和發(fā)送數(shù)據(jù),操作系統(tǒng)負(fù)責(zé)具體的收發(fā)數(shù)據(jù)工作,并把執(zhí)行結(jié)果發(fā)送到完成端口上。I/O操作線程不斷的讀取完成端口上的收發(fā)執(zhí)行結(jié)果,以確定下一步的操作,以此循環(huán)。

最后提到關(guān)閉完成端口,由于I/O操作線程是采用阻塞模式一直不停地讀取操作系統(tǒng)放在完成端口上的執(zhí)行結(jié)果,所以沒(méi)有新的收發(fā)數(shù)據(jù)工作執(zhí)行結(jié)果,完成端口則進(jìn)入死循環(huán)中不能退出,所有我們要采用發(fā)信息的方式來(lái)使完成端口關(guān)閉。

操作系統(tǒng)發(fā)送完成端口退出信息的API為PostQueuedCompletionStatus,實(shí)際上就是發(fā)送一個(gè)特定的網(wǎng)絡(luò)事件給I/O操作線程,讓完成端口自行退出。

代碼:

for (int i = 0; i < I/O操作線程數(shù)量; i++)

{ PostQueuedCompletionStatus(完成端口, 0, (DWORD) NULL, NULL); }

參考文獻(xiàn):

[1] 王艷平,張?jiān)?Windows 網(wǎng)絡(luò)與通信程序設(shè)計(jì)[M].人民郵電出版社,2013.

[2] 張?jiān)?Visual C++ 網(wǎng)絡(luò)程序設(shè)計(jì)實(shí)例詳解[M].人民郵電出版社,2013.

[3] Jeffrey Richter.Windows 核心編程 Windows via C/C++[M].清華大學(xué)出版社,2011.

[4] 徐磊,騰婧,張瑩.Windows Sockets網(wǎng)絡(luò)編程[M].機(jī)械工業(yè)出版社出版,2012.

汉源县| 大新县| 栖霞市| 乌兰察布市| 海宁市| 西宁市| 成武县| 鹿泉市| 泰来县| 菏泽市| 延川县| 澳门| 德格县| 淮阳县| 若尔盖县| 玉门市| 正宁县| 卢氏县| 临湘市| 绥中县| 宝坻区| 防城港市| 五大连池市| 五台县| 眉山市| 静安区| 黄石市| 黄大仙区| 灵川县| 杨浦区| 拉萨市| 阿拉善左旗| 河源市| 星座| 德令哈市| 二手房| 海林市| 绥芬河市| 友谊县| 平泉县| 江口县|