流格式套接字(SOCK_STREAM)
流格式套接字(Stream Sockets)也叫“面向連接的套接字”吵冒,在代碼中使用 SOCK_STREAM 表示。
SOCK_STREAM 是一種可靠的额划、雙向的通信數(shù)據(jù)流,數(shù)據(jù)可以準(zhǔn)確無誤地到達(dá)另一臺計(jì)算機(jī),如果損壞或丟失别渔,可以重新發(fā)送楣黍。
流格式套接字有自己的糾錯機(jī)制匾灶,在此我們就不討論了。
SOCK_STREAM 有以下幾個特征:
1.數(shù)據(jù)在傳輸過程中不會消失租漂;
2.數(shù)據(jù)是按照順序傳輸?shù)模?br>
3.數(shù)據(jù)的發(fā)送和接收不是同步的(有的教程也稱“不存在數(shù)據(jù)邊界”)阶女。
可以將 SOCK_STREAM 比喻成一條傳送帶颊糜,只要傳送帶本身沒有問題(不會斷網(wǎng)),就能保證數(shù)據(jù)不丟失秃踩;同時(shí)芭析,較晚傳送的數(shù)據(jù)不會先到達(dá),較早傳送的數(shù)據(jù)不會晚到達(dá)吞瞪,這就保證了數(shù)據(jù)是按照順序傳遞的馁启。
為什么流格式套接字可以達(dá)到高質(zhì)量的數(shù)據(jù)傳輸呢?這是因?yàn)樗褂昧?TCP 協(xié)議(The Transmission Control Protocol芍秆,傳輸控制協(xié)議)惯疙,TCP 協(xié)議會控制你的數(shù)據(jù)按照順序到達(dá)并且沒有錯誤。
你也許見過 TCP妖啥,是因?yàn)槟憬?jīng)常聽說“TCP/IP”霉颠。TCP 用來確保數(shù)據(jù)的正確性,IP(Internet Protocol荆虱,網(wǎng)絡(luò)協(xié)議)用來控制數(shù)據(jù)如何從源頭到達(dá)目的地蒿偎,也就是常說的“路由”。
那么怀读,“數(shù)據(jù)的發(fā)送和接收不同步”該如何理解呢诉位?
假設(shè)傳送帶傳送的是水果,接收者需要湊齊 100 個后才能裝袋菜枷,但是傳送帶可能把這 100 個水果分批傳送苍糠,比如第一批傳送 20 個,第二批傳送 50 個啤誊,第三批傳送 30 個岳瞭。接收者不需要和傳送帶保持同步,只要根據(jù)自己的節(jié)奏來裝袋即可蚊锹,不用管傳送帶傳送了幾批瞳筏,也不用每到一批就裝袋一次,可以等到湊夠了 100 個水果再裝袋牡昆。
流格式套接字的內(nèi)部有一個緩沖區(qū)(也就是字符數(shù)組)姚炕,通過 socket 傳輸?shù)臄?shù)據(jù)將保存到這個緩沖區(qū)。接收端在收到數(shù)據(jù)后并不一定立即讀取迁杨,只要數(shù)據(jù)不超過緩沖區(qū)的容量钻心,接收端有可能在緩沖區(qū)被填滿以后一次性地讀取,也可能分成好幾次讀取铅协。
也就是說捷沸,不管數(shù)據(jù)分幾次傳送過來,接收端只需要根據(jù)自己的要求讀取狐史,不用非得在數(shù)據(jù)到達(dá)時(shí)立即讀取痒给。傳送端有自己的節(jié)奏说墨,接收端也有自己的節(jié)奏,它們是不一致的苍柏。
流格式套接字有什么實(shí)際的應(yīng)用場景嗎尼斧?瀏覽器所使用的 http 協(xié)議就基于面向連接的套接字,因?yàn)楸仨氁_保數(shù)據(jù)準(zhǔn)確無誤试吁,否則加載的 HTML 將無法解析棺棵。
數(shù)據(jù)報(bào)格式套接字(SOCK_DGRAM)
數(shù)據(jù)報(bào)格式套接字(Datagram Sockets)也叫“無連接的套接字”,在代碼中使用 SOCK_DGRAM 表示熄捍。
計(jì)算機(jī)只管傳輸數(shù)據(jù)烛恤,不作數(shù)據(jù)校驗(yàn),如果數(shù)據(jù)在傳輸中損壞余耽,或者沒有到達(dá)另一臺計(jì)算機(jī)缚柏,是沒有辦法補(bǔ)救的。也就是說碟贾,數(shù)據(jù)錯了就錯了币喧,無法重傳。
因?yàn)閿?shù)據(jù)報(bào)套接字所做的校驗(yàn)工作少袱耽,所以在傳輸效率方面比流格式套接字要高杀餐。
SOCK_DGRAM 有以下幾個特征:
1.強(qiáng)調(diào)快速傳輸而非傳輸順序;
2.傳輸?shù)臄?shù)據(jù)可能丟失也可能損毀扛邑;
3.限制每次傳輸?shù)臄?shù)據(jù)大辛场;
4.數(shù)據(jù)的發(fā)送和接收是同步的(有的教程也稱“存在數(shù)據(jù)邊界”)蔬崩。
眾所周知,速度是快遞行業(yè)的生命搀暑。用摩托車發(fā)往同一地點(diǎn)的兩件包裹無需保證順序沥阳,只要以最快的速度交給客戶就行。這種方式存在損壞或丟失的風(fēng)險(xiǎn)自点,而且包裹大小有一定限制桐罕。因此,想要傳遞大量包裹桂敛,就得分配發(fā)送功炮。
另外,用兩輛摩托車分別發(fā)送兩件包裹术唬,那么接收者也需要分兩次接收薪伏,所以“數(shù)據(jù)的發(fā)送和接收是同步的”;換句話說粗仓,接收次數(shù)應(yīng)該和發(fā)送次數(shù)相同嫁怀。
總之设捐,數(shù)據(jù)報(bào)套接字是一種不可靠的、不按順序傳遞的塘淑、以追求速度為目的的套接字萝招。
數(shù)據(jù)報(bào)套接字也使用 IP 協(xié)議作路由,但是它不使用 TCP 協(xié)議存捺,而是使用 UDP 協(xié)議(User Datagram Protocol槐沼,用戶數(shù)據(jù)報(bào)協(xié)議)。
QQ 視頻聊天和語音聊天就使用 SOCK_DGRAM 來傳輸數(shù)據(jù)捌治,因?yàn)槭紫纫WC通信的效率母赵,盡量減小延遲,而數(shù)據(jù)的正確性是次要的具滴,即使丟失很小的一部分?jǐn)?shù)據(jù)凹嘲,視頻和音頻也可以正常解析,最多出現(xiàn)噪點(diǎn)或雜音构韵,不會對通信質(zhì)量有實(shí)質(zhì)的影響周蹭。
其實(shí)SOCK_DGRAM 沒有想象中的糟糕,不會頻繁的丟失數(shù)據(jù)疲恢,數(shù)據(jù)錯誤只是小概率事件凶朗。
面向連接和無連接的套接字到底有什么區(qū)別?
流格式套接字(Stream Sockets)就是“面向連接的套接字”显拳,它基于 TCP 協(xié)議棚愤;數(shù)據(jù)報(bào)格式套接字(Datagram Sockets)就是“無連接的套接字”,它基于 UDP 協(xié)議杂数。
這給大家造成一種印象宛畦,面向連接就是可靠的通信,無連接就是不可靠的通信揍移,實(shí)際情況是這樣嗎次和?
另外,不管是哪種數(shù)據(jù)傳輸方式那伐,都得通過整個 Internet 網(wǎng)絡(luò)的物理線路將數(shù)據(jù)傳輸過去踏施,從這個層面理解,所有的 socket都是有物理連接的呀罕邀,為什么還有無連接的 socket 呢畅形?
從字面上理解,面向連接好像有一條管道诉探,它連接發(fā)送端和接收端日熬,數(shù)據(jù)包都通過這條管道來傳輸。當(dāng)然阵具,兩臺計(jì)算機(jī)在通信之前必須先搭建好管道碍遍。
無連接好像沒頭蒼蠅亂撞定铜,數(shù)據(jù)包從發(fā)送端到接收端并沒有固定的線路,愛怎么走就怎么走怕敬,只要能到達(dá)就行揣炕。每個數(shù)據(jù)包都比較自私,不和別人分享自己的線路东跪,但是畸陡,大家最終都能殊途同歸,到達(dá)接收端虽填。
上圖是一個簡化的互聯(lián)網(wǎng)模型丁恭,H1 ~ H6 表示計(jì)算機(jī),A~E 表示路由器斋日,發(fā)送端發(fā)送的數(shù)據(jù)必須經(jīng)過路由器的轉(zhuǎn)發(fā)才能到達(dá)接收端牲览。
假設(shè) H1 要發(fā)送若干個數(shù)據(jù)包給 H6,那么有多條路徑可以選擇恶守,比如:
路徑①:H1 → A → C → E → H6
路徑②:H1 → A → B → E → H6
路徑③:H1 → A → B → D → E → H6
路徑④:H1 → A → B → C → E → H6
路徑⑤:H1→ A → C --> B → D → E→ H6
數(shù)據(jù)包的傳輸路徑是路由器根據(jù)算法來計(jì)算出來的第献,算法會考慮很多因素,比如網(wǎng)絡(luò)的擁堵狀況兔港、下一個路由器是否忙碌等庸毫。
無連接的套接字
對于無連接的套接字,每個數(shù)據(jù)包可以選擇不同的路徑衫樊,比如第一個數(shù)據(jù)包選擇路徑④飒赃,第二個數(shù)據(jù)包選擇路徑①,第三個數(shù)據(jù)包選擇路徑②等等科侈。當(dāng)然载佳,它們也可以選擇相同的路徑,那也只不過是巧合而已兑徘。
每個數(shù)據(jù)包之間都是獨(dú)立的刚盈,各走各的路,誰也不影響誰挂脑,除了迷路的或者發(fā)生意外的數(shù)據(jù)包,最后都能到達(dá) H6欲侮。但是崭闲,到達(dá)的順序是不確定的,比如:
1.第一個數(shù)據(jù)包選擇了一條比較長的路徑(比如路徑⑤)威蕉,第三個數(shù)據(jù)包選擇了一條比較短的路徑(比如路徑①)刁俭,雖然第一個數(shù)據(jù)包很早就出發(fā)了,但是走的路比較遠(yuǎn)韧涨,最終還是晚于第三個數(shù)據(jù)包達(dá)到牍戚。
2.第一個數(shù)據(jù)包選擇了一條比較短的路徑(比如路徑①)侮繁,第三個數(shù)據(jù)包選擇了一條比較長的路徑(比如路徑⑤),按理說第一個數(shù)據(jù)包應(yīng)該先到達(dá)如孝,但是非常不幸宪哩,第一個數(shù)據(jù)包走的路比較擁堵,這條路上的數(shù)據(jù)量非常大第晰,路由器處理得很慢锁孟,所以它還是晚于第三個數(shù)據(jù)包達(dá)到了。
還有一些意外情況會發(fā)生茁瘦,比如:
1.第一個數(shù)據(jù)包選擇了路徑①品抽,但是路由器C突然斷電了,那它就到不了 H6 了甜熔。
2.第三個數(shù)據(jù)包選擇了路徑②圆恤,雖然路不遠(yuǎn),但是太擁堵腔稀,以至于它等待的時(shí)間太長盆昙,路由器把它丟棄了。
總之烧颖,對于無連接的套接字弱左,數(shù)據(jù)包在傳輸過程中會發(fā)生各種不測,也會發(fā)生各種奇跡炕淮。H1 只負(fù)責(zé)把數(shù)據(jù)包發(fā)出拆火,至于它什么時(shí)候到達(dá),先到達(dá)還是后到達(dá)涂圆,有沒有成功到達(dá)们镜,H1 都不管了;H6 也沒有選擇的權(quán)利润歉,只能被動接收模狭,收到什么算什么,愛用不用踩衩。
無連接套接字遵循的是「盡最大努力交付」的原則嚼鹉,就是盡力而為,實(shí)在做不到了也沒辦法驱富。無連接套接字提供的沒有質(zhì)量保證的服務(wù)锚赤。
面向連接的套接字
面向連接的套接字在正式通信之前要先確定一條路徑,沒有特殊情況的話褐鸥,以后就固定地使用這條路徑來傳遞數(shù)據(jù)包了线脚。當(dāng)然,路徑被破壞的話,比如某個路由器斷電了浑侥,那么會重新建立路徑姊舵。
這條路徑是由路由器維護(hù)的,路徑上的所有路由器都要存儲該路徑的信息(實(shí)際上只需要存儲上游和下游的兩個路由器的位置就行)寓落,所以路由器是有開銷的括丁。
H1 和 H6 通信完畢后,要斷開連接零如,銷毀路徑躏将,這個時(shí)候路由器也會把之前存儲的路徑信息擦除。
在很多網(wǎng)絡(luò)通信教程中考蕾,這條預(yù)先建立好的路徑被稱為“虛電路”祸憋,就是一條虛擬的通信電路。
為了保證數(shù)據(jù)包準(zhǔn)確肖卧、順序地到達(dá)蚯窥,發(fā)送端在發(fā)送數(shù)據(jù)包以后,必須得到接收端的確認(rèn)才發(fā)送下一個數(shù)據(jù)包塞帐;如果數(shù)據(jù)包發(fā)出去了拦赠,一段時(shí)間以后仍然沒有得到接收端的回應(yīng),那么發(fā)送端會重新再發(fā)送一次葵姥,直到得到接收端的回應(yīng)荷鼠。這樣一來,發(fā)送端發(fā)送的所有數(shù)據(jù)包都能到達(dá)接收端榔幸,并且是按照順序到達(dá)的允乐。
發(fā)送端發(fā)送一個數(shù)據(jù)包,如何得到接收端的確認(rèn)呢削咆?很簡單牍疏,為每一個數(shù)據(jù)包分配一個 ID,接收端接收到數(shù)據(jù)包以后拨齐,再給發(fā)送端返回一個數(shù)據(jù)包鳞陨,告訴發(fā)送端我接收到了 ID 為 xxx 的數(shù)據(jù)包。
面向連接的套接字會比無連接的套接字多出很多數(shù)據(jù)包瞻惋,因?yàn)榘l(fā)送端每發(fā)送一個數(shù)據(jù)包厦滤,接收端就會返回一個數(shù)據(jù)包。此外歼狼,建立連接和斷開連接的過程也會傳遞很多數(shù)據(jù)包馁害。
不但是數(shù)量多了,每個數(shù)據(jù)包也變大了:除了源端口和目的端口蹂匹,面向連接的套接字還包括序號、確認(rèn)信號凹蜈、數(shù)據(jù)偏移限寞、控制標(biāo)志(通常說的 URG忍啸、ACK、PSH履植、RST计雌、SYN、FIN)玫霎、窗口凿滤、校驗(yàn)和、緊急指針庶近、選項(xiàng)等信息翁脆;而無連接的套接字則只包含長度和校驗(yàn)和信息。
有連接的數(shù)據(jù)包比無連接大很多鼻种,這意味著更大的負(fù)載和更大的帶寬反番。許多即時(shí)聊天軟件采用 UDP 協(xié)議(無連接套接字),與此有莫大的關(guān)系叉钥。
兩種套接字各有優(yōu)缺點(diǎn):
1.無連接套接字傳輸效率高罢缸,但是不可靠,有丟失數(shù)據(jù)包投队、搗亂數(shù)據(jù)的風(fēng)險(xiǎn)枫疆;
2.有連接套接字非常可靠敷鸦,萬無一失息楔,但是傳輸效率低,耗費(fèi)資源多轧膘。
參考文章:
套接字有哪些類型钞螟?socket有哪些類型?
套接字有哪些類型谎碍?socket有哪些類型鳞滨?
面向連接和無連接的套接字到底有什么區(qū)別?