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