由于http協(xié)議的簡單性以及業(yè)務(wù)的需要,我們不可避免地需要自己去實現(xiàn)一些走http協(xié)議的server。
當(dāng)我們的短連接http服務(wù)器(如驗證碼服務(wù)器)是客戶端是瀏覽器(ie、firefox、chrome)的時候,有一個問題需要特別注意,我稱之為短連接http服務(wù)器陷阱。首先,先來看下面一系列同一個請求,但是不同抓包工具抓取的內(nèi)容的截圖:
圖1.使用httpwatch抓包獲取的內(nèi)容
圖2.使用sniffer抓包獲取的內(nèi)容
其中,兩者是同一個請求,從sniffer抓包內(nèi)容看、瀏覽器和server已經(jīng)完成了交互過程。但是瀏覽器(包括ie、firefox、chrome)的Result卻是ERROR_INTERNET_CONNECTION_RESET,這是為什么呢?
通過走讀server代碼,發(fā)現(xiàn)server做了這樣一個邏輯,在有請求過來的時候只讀取了100個字節(jié),因為server認為這100個字節(jié)足夠判斷客戶端的請求行為(GET /HTTP/1.0 少于100個字節(jié)),而其它請求信息(包括cookie等等)是無用的,然后返回response信息,然后關(guān)閉連接。流程如下:
1)讀取100個字節(jié) => 2)返回response信息 => 3)關(guān)閉socket
當(dāng)客戶端請求的量不大的時候(內(nèi)網(wǎng)開發(fā)機并發(fā)請求量<100)、很容易出現(xiàn)上文所述http服務(wù)器陷阱的問題。當(dāng)客戶端請求的量很大的時候,出現(xiàn)上述問題的幾率倒不大,但是偶爾也會出現(xiàn)。分析原因如下:
當(dāng)并發(fā)量很少的時候,server讀取了100個字節(jié)并返回response后,馬上關(guān)閉連接。由于并發(fā)量少,server處理速度很快,這樣很快就會關(guān)閉了連接。但是,此時客戶端(瀏覽器)也許還有一些數(shù)據(jù)(例如cookie信息等)并沒有真正發(fā)送到server的內(nèi)核緩沖區(qū)。于是,瀏覽器繼續(xù)把剩余的數(shù)據(jù)試圖發(fā)送到server,但是,此時發(fā)現(xiàn)server已經(jīng)關(guān)閉了連接,就會引發(fā)ERROR_INTERNET_CONNECTION_RESET錯誤。雖然用sniffer抓包發(fā)現(xiàn)實際上整個交互過程已經(jīng)完成,但是瀏覽器缺認為這是一個致命的錯誤,獲取到的返回數(shù)據(jù)并沒有正確的顯示在瀏覽器上。
當(dāng)并發(fā)量很大的時候,雖然server只讀取了100個字節(jié),但是由于服務(wù)器的處理能力有限,從讀取了100字節(jié)到關(guān)閉連接的過程有一定的時間差。客戶端可以在這個時間差的時間內(nèi)把剩余的數(shù)據(jù)發(fā)送到了server內(nèi)核緩沖區(qū)(雖然應(yīng)用程序沒有讀取這部分數(shù)據(jù))。因此,出現(xiàn)ERROR_INTERNET_CONNECTION_RESET錯誤的概率就會變小。
總結(jié),在實現(xiàn)短連接的http服務(wù)器(尤其是提供給瀏覽器使用的服務(wù)器)時,盡量把http header數(shù)據(jù)包讀取完整,不要因為只需要一小部分header信息就能處理請求而不讀取其它的頭部信息,以此來加快處理速度或者節(jié)省空間。這往往適得其反,可能會引起一些列不可預(yù)料的問題。
由此,引發(fā)另一個思考,既然不可避免的需要讀取完整的http header數(shù)據(jù)包,那么,瀏覽器請求的時候,應(yīng)該盡量保持http header不要過于龐大,既是為了減緩server的壓力也是為了加快server的處理速度。而減少http header長度的最好方法就是減少cookie的內(nèi)容。