基于TCP流協(xié)議的數(shù)據(jù)包通訊 - 全文
一、TCP定義
TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC 793定義。在簡(jiǎn)化的計(jì)算機(jī)網(wǎng)絡(luò)OSI模型中,它完成第四層傳輸層所指定的功能,用戶數(shù)據(jù)報(bào)協(xié)議(UDP)是同一層另一個(gè)重要的傳輸協(xié)議。在因特網(wǎng)協(xié)議族(Internet protocol suite)中,TCP層是位于IP層之上,應(yīng)用層之下的中間層。不同主機(jī)的應(yīng)用層之間經(jīng)常需要可靠的、像管道一樣的連接,但是IP層不提供這樣的流機(jī)制,而是提供不可靠的包交換。
二、TCP可靠性實(shí)現(xiàn)
TCP提供一種面向連接的、可靠的字節(jié)流服務(wù)。面向連接意味著兩個(gè)使用TCP的應(yīng)用(通常是一個(gè)客戶和一個(gè)服務(wù)器)在彼此交換數(shù)據(jù)包之前必須先建立一個(gè)TCP連接。這一過(guò)程與打電話很相似,先撥號(hào)振鈴,等待對(duì)方摘機(jī)說(shuō)“喂”,然后才說(shuō)明是誰(shuí)。在一個(gè)TCP連接中,僅有兩方進(jìn)行彼此通信。廣播和多播不能用于TCP。
1.應(yīng)用數(shù)據(jù)被分割成TCP認(rèn)為最適合發(fā)送的數(shù)據(jù)塊。這和UDP完全不同,應(yīng)用程序產(chǎn)生的數(shù)據(jù)報(bào)長(zhǎng)度將保持不變。由TCP傳遞給I P的信息單位稱為報(bào)文段或段
2.當(dāng)TCP發(fā)出一個(gè)段后,它啟動(dòng)一個(gè)定時(shí)器,等待目的端確認(rèn)收到這個(gè)報(bào)文段。如果不能及時(shí)收到一個(gè)確認(rèn),將重發(fā)這個(gè)報(bào)文段。
3.當(dāng)TCP收到發(fā)自TCP連接另一端的數(shù)據(jù),它將發(fā)送一個(gè)確認(rèn)。這個(gè)確認(rèn)不是立即發(fā)送,通常將推遲幾分之一秒。
4.TCP將保持它首部和數(shù)據(jù)的檢驗(yàn)和。這是一個(gè)端到端的檢驗(yàn)和,目的是檢測(cè)數(shù)據(jù)在傳輸過(guò)程中的任何變化。如果收到段的檢驗(yàn)和有差錯(cuò),TCP將丟棄這個(gè)報(bào)文段和不確認(rèn)收到此報(bào)文段(希望發(fā)端超時(shí)并重發(fā))。
5.既然TCP報(bào)文段作為IP數(shù)據(jù)報(bào)來(lái)傳輸,而IP數(shù)據(jù)報(bào)的到達(dá)可能會(huì)失序,因此TCP報(bào)文段的到達(dá)也可能會(huì)失序。如果必要,TCP將對(duì)收到的數(shù)據(jù)進(jìn)行重新排序,將收到的數(shù)據(jù)以正確的順序交給應(yīng)用層。
6.既然I P數(shù)據(jù)報(bào)會(huì)發(fā)生重復(fù),TCP的接收端必須丟棄重復(fù)的數(shù)據(jù)。
7.TCP還能提供流量控制。TCP連接的每一方都有固定大小的緩沖空間。TCP的接收端只允許另一端發(fā)送接收端緩沖區(qū)所能接納的數(shù)據(jù)。這將防止較快主機(jī)致使較慢主機(jī)的緩沖區(qū)溢出。
三、TCP連接的建立
利用TCP傳輸數(shù)據(jù)前,需要建立tcp連接,tcp連接的建立有3個(gè)主要過(guò)程,叫做3次握手,具體過(guò)程如下圖所示:
過(guò)程:
1. 首先客戶端發(fā)送一個(gè)SYN包給服務(wù)器(SYN=1,Seq為主機(jī)選擇的這個(gè)連接的初始序號(hào)),然后等待應(yīng)答。
2. 服務(wù)器端收到SYN包,回應(yīng)給客戶端一個(gè)ACK =x+1、SYN=1的TCP數(shù)據(jù)段(ACK表示確認(rèn)序號(hào)有效,即收到上一個(gè)包,這里加1并不是ACK的值加1,ACK是一個(gè)標(biāo)志位,這里會(huì)變成1,而x+1則是希望收到的下一個(gè)包的序列號(hào),這個(gè)值放在包的確認(rèn)序列號(hào)字段中,而只有ACK=1時(shí),確認(rèn)序列號(hào)才有效)。
3. 客戶必須再次回應(yīng)服務(wù)器端一個(gè)ACK確認(rèn)數(shù)據(jù)段(這里的Seq為x+1)。
經(jīng)過(guò)上面3個(gè)過(guò)程就建立了一個(gè)tcp連接,接著就可以發(fā)送數(shù)據(jù)了,因?yàn)榻⑦B接使用了一個(gè)序列號(hào)x,所以發(fā)送數(shù)據(jù)的第一個(gè)字節(jié)序號(hào)為x+1。
注意:這里tcp為應(yīng)用層提供全雙工服務(wù),意味數(shù)據(jù)能在兩個(gè)方向上獨(dú)立地進(jìn)行傳輸,因此連接的每一段都有各自的傳輸數(shù)據(jù)序號(hào)(對(duì)應(yīng)于上圖中的x和y,這兩個(gè)值是沒有必然聯(lián)系的)。
四、TCP協(xié)議流的理解
TCP是流協(xié)議,不像UDP那樣sendto發(fā)一次消息,另一端必然會(huì)收到完整消息,或者沒有收到任何消息。當(dāng)用TCP send發(fā)一次消息的時(shí)候,可能另一端在某時(shí)刻可能只收到一部分消息,下一時(shí)刻才能收到另一部分。那如果一個(gè)消息很小,是否可以保證另一端在某時(shí)刻能收到這條完整消息?
調(diào)用send后,TCP將數(shù)據(jù)拷貝到緩沖區(qū)。緩沖區(qū)內(nèi)可能不止一條用戶消息。TCP按照一定算法,將緩沖區(qū)的數(shù)據(jù)打包到1-n個(gè)TCP報(bào)文中,交給IP層發(fā)送。TCP報(bào)文是TCP協(xié)議的最小發(fā)送單位,大小應(yīng)該是可變的,并且丟失的話會(huì)重發(fā)。并不能保證一個(gè)TCP報(bào)文中必然包含一條用戶消息的全部,所以即使消息很小,另一端也有可能在某時(shí)刻只收到部分
IP層將TCP報(bào)文裝進(jìn)IP包,然后再交給鏈路層發(fā)送以太幀
理論上IP包的大小應(yīng)該會(huì)選擇比MTU小。一旦IP包比MTU大,意味著網(wǎng)絡(luò)上的路由要幫你緩存多個(gè)以太幀,拼出IP包后才知道如何路由到下一個(gè)節(jié)點(diǎn)。向下一節(jié)點(diǎn)路由的時(shí)候還要再拆分成多個(gè)以太幀發(fā)送。所以TCP報(bào)文應(yīng)該會(huì)比選擇比MTU小。
五、基于TCP流協(xié)議的數(shù)據(jù)包通訊
TCP通訊是流協(xié)議,它不像UDP那樣基于包為邊界的通訊方式,TCP流式協(xié)議,舉個(gè)簡(jiǎn)單例子,一端用send 分別發(fā)送 100,123,120字節(jié)的數(shù)據(jù),另一端用recv可以一下子接收到 100+123+120=343字節(jié)的數(shù)據(jù),或者先接收 3個(gè)字節(jié)的數(shù)據(jù),再接收余下的340字節(jié),不管另一端怎么接收,最終是要接收到343字節(jié)的數(shù)據(jù)。而且TCP保證數(shù)據(jù)的完整性和順序,也就是兩端是數(shù)據(jù)同步的,出現(xiàn)任何一點(diǎn)的數(shù)據(jù)不一致,都會(huì)造成TCP連接的失效。
UDP則跟TCP大不一樣,他是基于包邊界的。所謂的包邊界,就是一端分別發(fā)送 100, 123, 120字節(jié)的數(shù)據(jù),另一端接收到也應(yīng)該分別是 100,123,120字節(jié)數(shù)據(jù)的三個(gè)包,不會(huì)出現(xiàn)一端發(fā)送100字節(jié)的一個(gè)數(shù)據(jù)包,另一端只接收到小于100字節(jié)的數(shù)據(jù)包,或者收到大于100字節(jié)的數(shù)據(jù)包。UDP同時(shí)也不保證穩(wěn)定和順序,如發(fā)送端發(fā)送100,123,120三個(gè)包,接收端可能接收到3個(gè)包,也可能只接收到2個(gè)包,也可能一個(gè)包也收不到,收到的順序不一定是100,123,120,可能是100,120,123,或者123,100,120等。這些TCP和UDP的屬性,大家稍微查查資料就該很清楚。
UDP的這種特殊通訊方式其實(shí)跟網(wǎng)絡(luò)底層鏈路層的通訊方式很接近。鏈路層的數(shù)據(jù)是一個(gè)數(shù)據(jù)包一個(gè)數(shù)據(jù)包的傳輸,并不保證數(shù)據(jù)能否達(dá)到對(duì)方,或者按照順序到達(dá)對(duì)方。UDP只是簡(jiǎn)單把鏈路層和IP層的數(shù)據(jù)加了一層封裝,加了端口用于識(shí)別同一個(gè)機(jī)器的不同進(jìn)程,UDP數(shù)據(jù)包的收發(fā)方式,只是組合成UDP包之后,簡(jiǎn)單的發(fā)送到底層網(wǎng)絡(luò)了事,至于底層網(wǎng)卡有沒有發(fā)成功或者接收成功,它是一概不聞不問(wèn)的。他的底層處理方式比起 TCP協(xié)議來(lái)說(shuō)簡(jiǎn)單太多了。
既然UDP這么好,編程又簡(jiǎn)單,可現(xiàn)在網(wǎng)絡(luò)中大部分都在使用TCP,一個(gè)非常重要的原因就是TCP提供的是可靠傳輸,TCP有一套復(fù)雜的底層算法來(lái)保證數(shù)據(jù)的完整和可靠,有這個(gè)理由就已經(jīng)足夠讓TCP在大部分場(chǎng)所比UDP好使了。因?yàn)榇蟛糠謺r(shí)候,我們?cè)陂_發(fā)網(wǎng)絡(luò)通信程序,都希望能隨意的接收和發(fā)送任意大小和完整的數(shù)據(jù),如果使用UDP,還得自己寫算法來(lái)保證數(shù)據(jù)的順序和完整,整個(gè)處理過(guò)程就等于實(shí)現(xiàn)一個(gè)小型的TCP協(xié)議。
一些特殊場(chǎng)所,比如P2P,各種使用P2P的下載軟件如迅雷等,這些軟件和傳統(tǒng)的服務(wù)端客戶端模式不大一樣,每個(gè)運(yùn)行軟件的機(jī)器既是客戶端也是服務(wù)端,而用戶的每個(gè)機(jī)器可能處于不同的網(wǎng)絡(luò)環(huán)境中,最典型的就是大部分機(jī)器處于NAT中,這樣的環(huán)境下,采用UDP是最佳選擇,因?yàn)門CP的NAT穿透能力差。
當(dāng)然這些軟件使用UDP,他們也必須實(shí)現(xiàn)一套算法來(lái)保證UDP傳輸?shù)耐暾晚樞?。我們?cè)陂_發(fā)TCP程序時(shí)候,最先想到的就是 請(qǐng)求-應(yīng)答模式:就是客戶端發(fā)起一個(gè)請(qǐng)求,然后服務(wù)端接收到請(qǐng)求,進(jìn)行處理,接著向客戶端應(yīng)答這個(gè)請(qǐng)求。最典型和常用的就是 HTTP協(xié)議,我們?yōu)g覽的所有網(wǎng)頁(yè),以及各種玲瑯滿目的網(wǎng)站,這些都是HTTP的功勞,HTTP協(xié)議是建立在TCP上的應(yīng)用層協(xié)議,采用就是 請(qǐng)求-應(yīng)答方式。
瀏覽器首先發(fā)起一個(gè)網(wǎng)頁(yè)請(qǐng)求的TCP連接,web服務(wù)器通過(guò)這個(gè)TCP連接應(yīng)答這個(gè)網(wǎng)頁(yè),并把網(wǎng)頁(yè)內(nèi)容傳輸給瀏覽器。然后瀏覽器可能關(guān)閉這個(gè)TCP連接,或者也可能利用這個(gè)TCP連接發(fā)起另外一個(gè)網(wǎng)頁(yè)請(qǐng)求。這個(gè)請(qǐng)求-應(yīng)答模式,也是我在使用TCP開發(fā)私有協(xié)議時(shí)候,使用的最多的模式,多得來(lái)以至于都忘記其他模式需求的存在了。
windows遠(yuǎn)程桌面:
使用遠(yuǎn)程桌面可以遠(yuǎn)程控制另一臺(tái)windows機(jī)器,可以在遠(yuǎn)程桌面里做任何本地桌面上的操作,比如刪除,復(fù)制文件,可以把本地文件復(fù)制到遠(yuǎn)程機(jī)器里,在復(fù)制的同時(shí)還能執(zhí)行其他操作,遠(yuǎn)程機(jī)器的桌面變化實(shí)時(shí)更新到本地,等等。
但是仔細(xì)研究會(huì)發(fā)現(xiàn),遠(yuǎn)程桌面只使用了一條 TCP連接,連接到被控制機(jī)器的 3389 端口。也就是在一條TCP通訊連接里,傳輸各種請(qǐng)求數(shù)據(jù)和接收各種應(yīng)答數(shù)據(jù)。遠(yuǎn)程桌面使用的是 RDP協(xié)議,我們這里不討論RDP的細(xì)節(jié),只討論如何在一條TCP連接中,如何做到遠(yuǎn)程桌面的各種操作。如果我們還是按照請(qǐng)求-應(yīng)答的模式來(lái)解釋遠(yuǎn)程桌面的通訊協(xié)議,顯然會(huì)有很多無(wú)法處理的問(wèn)題。
比如舉個(gè)簡(jiǎn)單例子:
我們?cè)谶h(yuǎn)程桌面客戶端點(diǎn)擊鼠標(biāo)操作,這個(gè)操作會(huì)通過(guò)3389的TCP連接發(fā)送到被控制端,如果按照請(qǐng)求-應(yīng)答模式來(lái)工作,則必須在被控制端接收到這個(gè)鼠標(biāo)操作,執(zhí)行這個(gè)動(dòng)作,然后回答給客戶端已經(jīng)執(zhí)行了這個(gè)操作。如果這期間,被控制機(jī)器的桌面界面內(nèi)容發(fā)生變化,則無(wú)法通知給客戶端,因?yàn)橐磺型ㄓ嵍际前凑湛蛻舳税l(fā)起請(qǐng)求,然后服務(wù)端應(yīng)答的方式通訊的。即使我們使用請(qǐng)求-應(yīng)答的方式,通過(guò)輪詢定時(shí)查詢被控制機(jī)器的界面內(nèi)容變化情況,也無(wú)法做到實(shí)時(shí),而且輪詢慢了會(huì)嚴(yán)重影響視覺效果,輪詢快了會(huì)嚴(yán)重浪費(fèi)資源。
于是,我們改換一種解決問(wèn)題的辦法,從 UDP 通訊的特點(diǎn):(按照包模式通訊)入手去解決上邊的問(wèn)題。假定我們?cè)谶h(yuǎn)程桌面的TCP通訊中,一切通訊的數(shù)據(jù)都定義成一個(gè)一個(gè)的單獨(dú)的數(shù)據(jù)包在同一條TCP連接中傳輸,數(shù)據(jù)包的接收和發(fā)送分開進(jìn)行,就是在同一個(gè)TCP連接中,一個(gè)線程專門接收數(shù)據(jù)包,一個(gè)線程專門發(fā)送數(shù)據(jù)包。這是可以的,因?yàn)楝F(xiàn)在的網(wǎng)卡都是工作在全雙工狀態(tài)下。所謂全雙工,就是接收和發(fā)送使用各自的通道,能獨(dú)立進(jìn)行數(shù)據(jù)傳輸。
大致偽代碼如下:
int tcp_socket = 客戶端連接到服務(wù)端的socket或者服務(wù)端接收到客戶端連接的socket。
receive_thread() //負(fù)責(zé)接收數(shù)據(jù)包的線程
{
tcp_packet = recv_packet (tcp_socket );
////TCP 是流協(xié)議,因此,我們必須至少定義一個(gè)表示包大小的頭+包內(nèi)容,才能保證TCP數(shù)據(jù)傳輸?shù)耐健?/p>
//處理 tcp_packet 包,為了不阻塞讀線程,一般是把tcp_packet交給別的線程處理。
}
send_thread()//負(fù)責(zé)發(fā)送數(shù)據(jù)包的線程
{
while(loop){
從發(fā)送隊(duì)列取出一個(gè)包 tcp_packet,(發(fā)送隊(duì)列,是別的線程生成的需要發(fā)送的數(shù)據(jù)包。)
send_packet( tcp_socket, tcp_packet ); //發(fā)送這個(gè)數(shù)據(jù)包。
}
}
再回到上邊的問(wèn)題,
遠(yuǎn)程桌面控制端(客戶端)和被控制端(服務(wù)端),分別開啟兩個(gè)線程,一個(gè)負(fù)責(zé)接收數(shù)據(jù)包,一個(gè)負(fù)責(zé)發(fā)送數(shù)據(jù)包。當(dāng)我們?cè)谶h(yuǎn)程桌面客戶端點(diǎn)擊鼠標(biāo)等操作,生成一個(gè)鼠標(biāo)的數(shù)據(jù)包投遞到發(fā)送線程,發(fā)送線程再把它傳輸?shù)奖豢刂贫?,被控制端接收到這個(gè)數(shù)據(jù)包,然后執(zhí)行,他如果要回復(fù)這個(gè)鼠標(biāo)的執(zhí)行結(jié)果,則再生成一個(gè)結(jié)果包投遞到發(fā)送線程,發(fā)送線程再把這個(gè)包傳輸給客戶端。
同時(shí)如果被控制端的界面發(fā)生改變,則生成一個(gè)界面內(nèi)容改變的數(shù)據(jù)包,投遞到發(fā)送線程,發(fā)送線程再傳輸給客戶端??蛻舳说慕邮站€程接收到界面內(nèi)容改變的數(shù)據(jù)包,顯示新的被控制端的界面內(nèi)容??蛻舳私邮盏绞髽?biāo)執(zhí)行結(jié)果的包,知道鼠標(biāo)操作是失敗還是成功。
按照包的方式通訊,就能在遠(yuǎn)程桌面中傳遞各種復(fù)雜的動(dòng)作,每個(gè)動(dòng)作都封裝成一個(gè)一個(gè)的數(shù)據(jù)包進(jìn)行傳輸。接收包和發(fā)送包分開獨(dú)立進(jìn)行,互相不干擾,每個(gè)包是否需要應(yīng)答包,根據(jù)每個(gè)包的需求決定,不是必須的。這又回到了 UDP通訊方式。那為何不干脆使用UDP代替呢? 還是上邊提到的原因,TCP保證穩(wěn)定和順序,這點(diǎn)在遠(yuǎn)程桌面等這類要求數(shù)據(jù)必須準(zhǔn)確的地方,是十分必要的。
TCP如何保證傳輸?shù)氖菃为?dú)數(shù)據(jù)包?
每個(gè)數(shù)據(jù)包定義成 ”包大小+包內(nèi)容“,比如4個(gè)字節(jié)表示包的大小,然后是包數(shù)據(jù)。
發(fā)送的時(shí)候,“包大小+包內(nèi)容”組合到一起發(fā)送,接收的時(shí)候,先接收固定的4個(gè)字節(jié),獲取到包的size,
然后再接收size字節(jié)的數(shù)據(jù),這樣一個(gè)包就接收完成。大致偽代碼如下:
send_packet(tcp_socket, packet, packet_size) //發(fā)送數(shù)據(jù)包
{
int32 size = pakcet_size; ///應(yīng)該采用網(wǎng)絡(luò)序
send(tcp_socket, &size, 4.。);
send(tcp_socket, packet, packet_size);
}
recv_packet(tcp_socket)
{
int32 size;
recv(tcp_socket, &size, 4,。。.);
char* packet = malloc(size);
recv( tcp_socket, packet, size, 。。.);
return packet;
}
- 第 1 頁(yè):基于TCP流協(xié)議的數(shù)據(jù)包通訊
- 第 2 頁(yè):windows遠(yuǎn)程桌面
本文導(dǎo)航
非常好我支持^.^
(1) 100%
不好我反對(duì)
(0) 0%
相關(guān)閱讀:
- [電子說(shuō)] 中興通訊助力高質(zhì)量共建“一帶一路” 2023-10-24
- [電子說(shuō)] 無(wú)線模擬信號(hào)采集器0-10v0-5v 4-20mA數(shù)據(jù)wifi通訊 2023-10-24
- [智能電網(wǎng)] 安科瑞AISD系列智能安全配電裝置案例 2023-10-24
- [電子說(shuō)] 雷迪埃 OCTIS 在通訊行業(yè)Open Ran中的應(yīng)用 2023-10-23
- [電子說(shuō)] 3線串行數(shù)據(jù)通訊EEPROM的使用 2023-10-23
- [電子說(shuō)] 近場(chǎng)通訊結(jié)構(gòu)解析 2023-10-23
- [電子說(shuō)] 多種PLC之間跨網(wǎng)段通訊的解決方案 2023-10-23
- [電子說(shuō)] 禧瑪諾領(lǐng)先600Pro儀表及其整車電氣通訊方案的拆解分析 2023-10-23
( 發(fā)表人:姚遠(yuǎn)香 )