深入學習 下 java socket

 底層數據結構

    如果不理解套接字的具體實現所關聯的數據結構和底層協議的工作細節,就很難抓住網絡編程的精妙之處,對於TCP套接字來說,更是如此。套接字所關聯的底層的數據結構集包含了特定Socket實例所關聯的信息。比附,套接字結構除其他信息外還包含:

    1、該套接字所關聯的本地和遠程互聯網地址和端口號。

    2、一個FIFO(First Im First Out)隊列,用於存放接收到的等待分配的數據,以及一個用於存放等待傳輸的數據的隊列。

    3、對於TCP套接字,還包含了與打開和關閉TCP握手相關的額定協議狀態信息。


    瞭解這些數據結構,以及底層協議如何對其進行影響是非常有用的,因爲它們控制了各種Socket對象行爲的各個方面。例如,由於TCP提供了一種可信賴的字節流服務,任何寫入Socket和OutpitStream的數據副本都必須保留,直到連接的另一端將這些數據成功接收。向輸出流寫數據並不意味着數據實際上已經被髮送——它們只是被複制到了本地緩衝區,就算在Socket的OutputStream上進行flush()操作,也不能保證數據能夠立即發送到信道。此外,字節流服務的自身屬性決定了其無法保留輸入流中消息的邊界信息。


    數據傳輸的底層實現

    在使用TCP套接字時,需要記住的最重要的一點是:不能假設在連接的一端將數據寫入輸出流和在另一端從輸入流讀出數據之間有任何的一致性。尤其是在發送端由單個輸出流的write()方法傳輸的數據,可能會通過另一端的多個輸入流的read()方法獲取,而一個read()方法可能會返回多個write()方法傳輸的數據。

    一般來講,我們可以認爲TCP連接上發送的所有字節序列在某一瞬間被分成了3個FIFO隊列:

    1、SendQ:在發送端底層實現中緩存的字節,這些字節已經寫入輸出流,但還沒在接收端成功接收。它佔用大約37KB內存。

    2、RecvQ:在接收端底層實現中緩存的字節,這些字節等待分配到接收程序——即從輸入流中讀取。它佔用大約25KB內存。

    3、Delivered:接收者從輸入流已經讀取到的字節。

    當我們調用OutputStream的write()方法時,將向SendQ追加字節。

    TCP協議負責將字節按順序從SendQ移動到RecvQ。這裏有重要的一點需要明確:這個轉移過程無法由用戶程序控制或直接觀察到,並且在塊中發生,這些塊的大小在一定程度上獨立於傳遞給write()方法的緩衝區大小。

    接收程序從Socket的InputStream讀取數據時,字節就從RecvQ移動到Delivered中,而轉移的塊的大小依賴於RecvQ中的數據量和傳遞給read()方法的緩衝區的大小。


   示例分析

     爲了展示這種情況,考慮如下程序:



     其中,圓點代表了設置緩衝區數據的代碼,但不包含對out.write()方法的調用。這個TCP連接向接收端傳輸8000字節,在連接的接收端,這8000字節的分組方式取決於連接兩端的out.write()方法和in.read()方法的調用時間差,以及提供給in.read()方法的緩衝區的大小。

    下圖展示了3次調用out.write()方法後,另一端調用in.read()方法前,以上3個隊列的一種可能狀態。不同的陰影效果分別代表了上文中3次調用write()方法傳輸的不同數據:



     現在假設接收者調用read()方法時使用的緩衝區數組大小爲2000字節,read()調用則將把RecvQ中的1500字節全部移動到數組中,返回值爲1500。注意,這些數據中包含了第一次和第二次調用write()方法時傳輸的字節,再過一段時間,當TCP連接傳完更多數據後,這三部分的狀態可能如下圖所示:



     如果接收者現在調用read()方法時使用4000字節的緩衝區數組,將有很多字節從RecvQ隊列轉移到Delivered隊列中,這包括第二次調用write()方法時剩下的1500字節加上第三次調用write()方法的錢2500字節。此時,隊列的狀態如下圖:



     下次調用read()方法返回的字節數,取決於緩衝區數組的大小,亦及發送方套接字通過網絡向接收方實現傳輸數據的時機。數據從sendQ到RecvQ緩衝區的移動過程對應用程序協議的設計有重要的指導性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章