面向報(bào)文(UDP)和面向字節(jié)流(TCP)的區(qū)別
面向報(bào)文的傳輸方式是應(yīng)用層交給UDP多長(zhǎng)的報(bào)文,UDP就照樣發(fā)送,即一次發(fā)送一個(gè)報(bào)文。因此,應(yīng)用程序必須選擇合適大小的報(bào)文。若報(bào)文太長(zhǎng),則IP層需要分片,降低效率。若太短,會(huì)是IP太小。UDP對(duì)應(yīng)用層交下來(lái)的報(bào)文,既不合并,也不拆分,而是保留這些報(bào)文的邊界。這也就是說(shuō),應(yīng)用層交給UDP多長(zhǎng)的報(bào)文,UDP就照樣發(fā)送,即一次發(fā)送一個(gè)報(bào)文。
面向字節(jié)流的話,雖然應(yīng)用程序和TCP的交互是一次一個(gè)數(shù)據(jù)塊(大小不等),但TCP把應(yīng)用程序看成是一連串的無(wú)結(jié)構(gòu)的字節(jié)流。TCP有一個(gè)緩沖,當(dāng)應(yīng)用程序傳送的數(shù)據(jù)塊太長(zhǎng),TCP就可以把它劃分短一些再傳送。如果應(yīng)用程序一次只發(fā)送一個(gè)字節(jié),TCP也可以等待積累有足夠多的字節(jié)后再構(gòu)成報(bào)文段發(fā)送出去。
下圖是TCP和UDP協(xié)議的一些應(yīng)用。

下圖是TCP和UDP協(xié)議的比較。

這里再詳細(xì)說(shuō)一下面向連接和面向無(wú)連接的區(qū)別:
面向連接舉例:兩個(gè)人之間通過(guò)電話進(jìn)行通信;
面向無(wú)連接舉例:郵政服務(wù),用戶把信函放在郵件中期待郵政處理流程來(lái)傳遞郵政包裹。顯然,不可達(dá)代表不可靠。
從程序?qū)崿F(xiàn)的角度來(lái)看,可以用下圖來(lái)進(jìn)行描述。

從上圖也能清晰的看出,TCP通信需要服務(wù)器端偵聽(tīng)listen、接收客戶端連接請(qǐng)求accept,等待客戶端connect建立連接后才能進(jìn)行數(shù)據(jù)包的收發(fā)(recv/send)工作。而UDP則服務(wù)器和客戶端的概念不明顯,服務(wù)器端即接收端需要綁定端口,等待客戶端的數(shù)據(jù)的到來(lái)。后續(xù)便可以進(jìn)行數(shù)據(jù)的收發(fā)(recvfrom/sendto)工作。
在前面講解UDP時(shí),提到了UDP保留了報(bào)文的邊界,下面我們來(lái)談?wù)凾CP和UDP中報(bào)文的邊界問(wèn)題。在默認(rèn)的阻塞模式下,TCP無(wú)邊界,UDP有邊界。
● 對(duì)于TCP協(xié)議,客戶端連續(xù)發(fā)送數(shù)據(jù),只要服務(wù)端的這個(gè)函數(shù)的緩沖區(qū)足夠大,會(huì)一次性接收過(guò)來(lái),即客戶端是分好幾次發(fā)過(guò)來(lái),是有邊界的,而服務(wù)端卻一次性接收過(guò)來(lái),所以證明是無(wú)邊界的;
● 而對(duì)于UDP協(xié)議,客戶端連續(xù)發(fā)送數(shù)據(jù),即使服務(wù)端的這個(gè)函數(shù)的緩沖區(qū)足夠大,也只會(huì)一次一次的接收,發(fā)送多少次接收多少次,即客戶端分幾次發(fā)送過(guò)來(lái),服務(wù)端就必須按幾次接收,從而證明,這種UDP的通訊模式是有邊界的。
TCP無(wú)邊界,造成對(duì)采用TCP協(xié)議發(fā)送的數(shù)據(jù)進(jìn)行接收比較麻煩,在接收的時(shí)候易出現(xiàn)粘包,即發(fā)送方發(fā)送的若干包數(shù)據(jù)到接收方接收時(shí)粘成一包。由于TCP是流協(xié)議,對(duì)于一個(gè)socket的包,如發(fā)送 10AAAAABBBBB兩次,由于網(wǎng)絡(luò)原因***次又分成兩次發(fā)送, 10AAAAAB和BBBB,如果接包的時(shí)候先讀取10(包長(zhǎng)度)再讀入后續(xù)數(shù)據(jù),當(dāng)接收得快,發(fā)送的慢時(shí),就會(huì)出現(xiàn)先接收了 10AAAAAB,會(huì)解釋錯(cuò)誤 ,再接到BBBB10AAAAABBBBB,也解釋錯(cuò)誤的情況。這就是TCP的粘包。
在網(wǎng)絡(luò)傳輸應(yīng)用中,通常需要在網(wǎng)絡(luò)協(xié)議之上再自定義一個(gè)協(xié)議封裝一下,簡(jiǎn)單做法就是在要發(fā)送的數(shù)據(jù)前面再加一個(gè)自定義的包頭,包頭中可以包含數(shù)據(jù)長(zhǎng)度和其它一些信息,接收的時(shí)候先收包頭,再根據(jù)包頭中描述的數(shù)據(jù)長(zhǎng)度來(lái)接收后面的數(shù)據(jù)。詳細(xì)做法是:先接收包頭,在包頭里指定包體長(zhǎng)度來(lái)接收。設(shè)置包頭包尾的檢查位( 比如以0xAA開(kāi)頭,0xCC結(jié)束來(lái)檢查一個(gè)包是否完整)。對(duì)于TCP來(lái)說(shuō):
1)不存在丟包,錯(cuò)包,所以不會(huì)出現(xiàn)數(shù)據(jù)出錯(cuò) ;
2)如果包頭檢測(cè)錯(cuò)誤,即為非法或者請(qǐng)求,直接重置即可。
為了避免粘包現(xiàn)象,可采取以下幾種措施。
一、對(duì)于發(fā)送方引起的粘包現(xiàn)象,用戶可通過(guò)編程設(shè)置來(lái)避免,TCP提供了強(qiáng)制數(shù)據(jù)立即傳送的操作指令push,TCP軟件收到該操作指令后,就立即將本段數(shù)據(jù)發(fā)送出去,而不必等待發(fā)送緩沖區(qū)滿;
二、對(duì)于接收方引起的粘包,則可通過(guò)優(yōu)化程序設(shè)計(jì)、精簡(jiǎn)接收進(jìn)程工作量、提高接收進(jìn)程優(yōu)先級(jí)等措施,使其及時(shí)接收數(shù)據(jù),從而盡量避免出現(xiàn)粘包現(xiàn)象;
三、由接收方控制,將一包數(shù)據(jù)按結(jié)構(gòu)字段,人為控制分多次接收,然后合并,通過(guò)這種手段來(lái)避免粘包。