網路程式設計中如何得知一次請求(或響應)的資料已接收完

2022-01-11 18:47:32 字數 1586 閱讀 7618

兩年前用.net 2.0做了乙個反向**伺服器,在這兩年時間裡,不斷修改bug以及優化效能,使得可用性大大提高。近來碰到乙個功能需求,實在無法找出有效的解決辦法,只好上來請教各位高人。

先說說反向**的工作機理吧。

1、客戶端通過瀏覽器訪問反向**的時候,會發出乙個http請求,反向**收到這個tcp連線的時候,建立乙個新的會話用於處理這個請求(beginaccept、endaccept);

2、會話物件建立乙個從客戶端接收資料的委託,開始非同步讀取資料(beginread);

3、取得資料時,進入非同步讀取的**函式中,開始處理資料(endread);

4、檢查反向**與伺服器的連線是否已建立,如果沒有建立,那麼需要先建立連線(connectserver),並建立伺服器的非同步讀取委託(beginread);

5、把資料非同步寫入伺服器(beginwrite);

6、重新建立客戶端非同步讀取委託(beginread),回到3;

7、收到伺服器返回資料時,處理後,非同步寫入客戶端(beginwrite);

8、重新建立伺服器非同步讀取委託(beginread),回到7;

所有的資料傳輸,都使用非同步來完成,而只需要在3和7處為業務編寫資料處理**即可。

實際上,對於反向**來說,只需要處理客戶端發來的資料就可以了,需要把http的host頭替換為真實伺服器,而對於伺服器響應的資料,只需要原樣傳送給客戶端就可以了。

在步驟3中,我們只知道當前收到了客戶端發來的資料,而不知道這個資料是不是http請求頭,或者是完整的http請求頭。幸好,對於反向**來說,不需要關心是否是完整的http請求頭,只需要檢查是否是http請求頭,如果是,就修改host即可。在這裡,我假設http請求的第乙個資料報肯定是獨立的資料報,不會「粘」在tcp連線中上一次資料的後面,這樣就可以直接使用http協議規定的格式來檢查這個資料報是否http請求頭了。雖然這個假設沒有什麼依據,但它確實非常有效。

程式就這樣工作了兩年,沒有什麼問題。

但接下來,問題就出現了,有乙個需求,要求能夠把伺服器返回的頁面中的某個字串替換為指定的字串。比如我用反向**指向,我就需要把頁面中所有使用了絕對路徑的連線修改為指向反向**伺服器的連線。這就要求在步驟7這裡處理資料,把資料轉為字串,然後替換鏈結,然後才發往客戶端。

但步驟7每次收到的資料只是乙個片段,而不是整個頁面的html。即使我們再次假設http響應的第乙個資料報是獨立的資料報,也只能識別哪些是響應頭,哪些是資料體而已。也想過每一段資料轉為這一段的字串進行處理,但是,如果剛好某個字元被網路層拆分到兩個tcp資料報裡怎麼辦?還有,想這樣使用了gzip的,如果不接受完整個頁面的資料,是無法解壓的;就算這兩種情況都不存在,而網路層剛好在超連結的地方拆分資料報怎麼辦?

因此,最保守的做法就是拿到整個頁面資料再開始處理。也想過http響應頭那裡有個content-length指明內容長度的,但實際中,很多響應根本就不到這個段。

我檢視過httplistener類和httplistenerrequest類,嘗試從中發現它是如何接受完一次請求(響應)的,可惜這兩個類呼叫了大量nativeapi,就無法得知了。

還有瀏覽器,它又是如何得知某次響應是否已經完成的呢?

還請各位高人多多指教!

這個**已經放到codeplex上,大家有興趣可以看看:

php如何實現只替換一次或N次

我們都知道,在php裡strtr,strreplace等函式都可以用來替換,不過他們每次替換的時候都是全部替換,舉個例子 abcabbc 這個字串如果使用上邊的函式來把其中的b替換掉,那麼他會全部替換掉,但是如果你想只替換乙個或兩個怎麼辦呢?看下邊的解決方法 這是個比較有點意思的問題,正好之前也做過...

網路程式設計 socket第複習一次

伺服器端 一次會話 import socket 建立socket物件 soc socket.socket 繫結ip和埠號 soc.bind 6666 開始監聽 soc.listen while true print 等待連線 等待連線 conn,add soc.accept print 連線成功 w...

一次網路請求的完整生命週期

從客戶端發起請求的時候,網路資料流是從上而下的 訪問 瀏覽器位址列輸入 這只是 網域名稱,瀏覽器並不知道要去 訪問資源,這個時候用到dns協議對網域名稱進行解析,在網域名稱註冊商那裡繫結了網域名稱對應的ip位址,ip位址就相當於網際網路世界的門牌號。知道了目標ip後,瀏覽器打包本次請求,根據傳輸資料...