轉(zhuǎn)載請注明出處:http://blog.csdn.net/ns_code/article/details/15813809
? ? 如果不理解套接字的具體實現(xiàn)所關(guān)聯(lián)的數(shù)據(jù)結(jié)構(gòu)和底層協(xié)議的工作細(xì)節(jié)授药,就很難抓住網(wǎng)絡(luò)編程的精妙之處随静,對于TCP套接字來說莺褒,更是如此闰非。套接字所關(guān)聯(lián)的底層的數(shù)據(jù)結(jié)構(gòu)集包含了特定Socket實例所關(guān)聯(lián)的信息括细。比附僚楞,套接字結(jié)構(gòu)除其他信息外還包含:
? ? 1岸裙、該套接字所關(guān)聯(lián)的本地和遠(yuǎn)程互聯(lián)網(wǎng)地址和端口號建芙。
? ? 2来累、一個FIFO(First Im First Out)隊列砚作,用于存放接收到的等待分配的數(shù)據(jù),以及一個用于存放等待傳輸?shù)臄?shù)據(jù)的隊列嘹锁。
? ? 3葫录、對于TCP套接字,還包含了與打開和關(guān)閉TCP握手相關(guān)的額定協(xié)議狀態(tài)信息领猾。
? ? 了解這些數(shù)據(jù)結(jié)構(gòu)米同,以及底層協(xié)議如何對其進(jìn)行影響是非常有用的,因為它們控制了各種Socket對象行為的各個方面摔竿。例如窍霞,由于TCP提供了一種可信賴的字節(jié)流服務(wù),任何寫入Socket和OutpitStream的數(shù)據(jù)副本都必須保留拯坟,直到連接的另一端將這些數(shù)據(jù)成功接收但金。向輸出流寫數(shù)據(jù)并不意味著數(shù)據(jù)實際上已經(jīng)被發(fā)送——它們只是被復(fù)制到了本地緩沖區(qū),就算在Socket的OutputStream上進(jìn)行flush()操作郁季,也不能保證數(shù)據(jù)能夠立即發(fā)送到信道冷溃。此外,字節(jié)流服務(wù)的自身屬性決定了其無法保留輸入流中消息的邊界信息梦裂。
? ? 數(shù)據(jù)傳輸?shù)牡讓訉崿F(xiàn)
在使用TCP套接字時似枕,需要記住的最重要的一點是:不能假設(shè)在連接的一端將數(shù)據(jù)寫入輸出流和在另一端從輸入流讀出數(shù)據(jù)之間有任何的一致性。尤其是在發(fā)送端由單個輸出流的write()方法傳輸?shù)臄?shù)據(jù)年柠,可能會通過另一端的多個輸入流的read()方法獲取凿歼,而一個read()方法可能會返回多個write()方法傳輸?shù)臄?shù)據(jù)。
? ? 一般來講冗恨,我們可以認(rèn)為TCP連接上發(fā)送的所有字節(jié)序列在某一瞬間被分成了3個FIFO隊列:
? ? 1答憔、SendQ:在發(fā)送端底層實現(xiàn)中緩存的字節(jié),這些字節(jié)已經(jīng)寫入輸出流掀抹,但還沒在接收端成功接收虐拓。它占用大約37KB內(nèi)存。
? ? 2傲武、RecvQ:在接收端底層實現(xiàn)中緩存的字節(jié)蓉驹,這些字節(jié)等待分配到接收程序——即從輸入流中讀取城榛。它占用大約25KB內(nèi)存。
? ? 3态兴、Delivered:接收者從輸入流已經(jīng)讀取到的字節(jié)狠持。
?當(dāng)我們調(diào)用OutputStream的write()方法時,將向SendQ追加字節(jié)瞻润。
? ? TCP協(xié)議負(fù)責(zé)將字節(jié)按順序從SendQ移動到RecvQ喘垂。這里有重要的一點需要明確:這個轉(zhuǎn)移過程無法由用戶程序控制或直接觀察到,并且在塊中發(fā)生敢订,這些塊的大小在一定程度上獨立于傳遞給write()方法的緩沖區(qū)大小王污。
? ? 接收程序從Socket的InputStream讀取數(shù)據(jù)時,字節(jié)就從RecvQ移動到Delivered中楚午,而轉(zhuǎn)移的塊的大小依賴于RecvQ中的數(shù)據(jù)量和傳遞給read()方法的緩沖區(qū)的大小昭齐。
? ? ?為了展示這種情況,考慮如下程序:
? ? ?其中矾柜,圓點代表了設(shè)置緩沖區(qū)數(shù)據(jù)的代碼阱驾,但不包含對out.write()方法的調(diào)用。這個TCP連接向接收端傳輸8000字節(jié)怪蔑,在連接的接收端里覆,這8000字節(jié)的分組方式取決于連接兩端的out.write()方法和in.read()方法的調(diào)用時間差,以及提供給in.read()方法的緩沖區(qū)的大小缆瓣。
? ? 下圖展示了3次調(diào)用out.write()方法后喧枷,另一端調(diào)用in.read()方法前,以上3個隊列的一種可能狀態(tài)弓坞。不同的陰影效果分別代表了上文中3次調(diào)用write()方法傳輸?shù)牟煌瑪?shù)據(jù):
? ? ?現(xiàn)在假設(shè)接收者調(diào)用read()方法時使用的緩沖區(qū)數(shù)組大小為2000字節(jié)隧甚,read()調(diào)用則將把RecvQ中的1500字節(jié)全部移動到數(shù)組中,返回值為1500渡冻。注意戚扳,這些數(shù)據(jù)中包含了第一次和第二次調(diào)用write()方法時傳輸?shù)淖止?jié),再過一段時間族吻,當(dāng)TCP連接傳完更多數(shù)據(jù)后帽借,這三部分的狀態(tài)可能如下圖所示:
? ? ?如果接收者現(xiàn)在調(diào)用read()方法時使用4000字節(jié)的緩沖區(qū)數(shù)組,將有很多字節(jié)從RecvQ隊列轉(zhuǎn)移到Delivered隊列中超歌,這包括第二次調(diào)用write()方法時剩下的1500字節(jié)加上第三次調(diào)用write()方法的錢2500字節(jié)砍艾。此時,隊列的狀態(tài)如下圖:
? ? ?下次調(diào)用read()方法返回的字節(jié)數(shù)握础,取決于緩沖區(qū)數(shù)組的大小辐董,亦及發(fā)送方套接字通過網(wǎng)絡(luò)向接收方實現(xiàn)傳輸數(shù)據(jù)的時機(jī)。數(shù)據(jù)從sendQ到RecvQ緩沖區(qū)的移動過程對應(yīng)用程序協(xié)議的設(shè)計有重要的指導(dǎo)性禀综。