傳輸層:負責數據能夠從發送端傳到接收端(只需要關注點對點的傳輸,中間的傳輸過程一概不管)
UDP和TCP
UDP(全雙工):
1.無連接,2不可靠,3.面向數據報
分別表示UDP源端口號、目的端口號、UDP長度、UDP檢驗和
端口號在傳輸層的概念:區分這個數據要交給哪個程序去處理,啟動一個服務器,會和一個端口號相綁定,一般HTTP綁定在80端口號,來區分發件人和收件人
UDP的報文長度最多是64k,這是一個比較小的數字,這就限制了應用層協議的數據長度,一旦數據長度超出了UDP的表示范圍,就會出現問題
可以在應用層通過代碼,將數據拆分成多個數據報,再使用多個UDP數據報來分別發送
代碼實現的成本大大提高了
校驗和(檢驗和):比較簡短,同時最好能和內容相關聯
UDP中使用的是CRC循環冗余校驗的方式
uint16_tchecksum1 =0; for(依次遍歷數據包的每個字節){ checksum+=當前字節的值 }
發送者在發送前先計算了一個校驗和checksum1
就把數據和checksum1一起發送到對端
接收端也按照相同的規則計算校驗和checksum2
對比checksum1和checksum2是否相同
校驗和出錯就會直接丟棄
UDP的特點:
無連接:知道對端的IP的端口號就可以直接傳輸,不需要建立連接
不可靠:沒有確認機制,沒有重傳機制,如果因為網絡故障該段無法發送到對端,UDP協議也不會給應用層返回任何錯誤信息
面向數據報:不能靈活的控制讀寫數據的次數和數量
面向數據報
應用層交給UDP多長的報文,UDP原樣發送,既不會拆分,也不會合並
用UDP傳輸100個字節的數據:
如果發送端調用一次sendto, 發送100個字節, 那么接收端也必須調用對應的一次recvfrom, 接收100個字
節; 而不能循環調用10次recvfrom, 每次接收10個字節
基於UDP的應用層協議
當然, 也包括你自己寫UDP程序時自定義的應用層協議
NFS: 網絡文件系統
TFTP: 簡單文件傳輸協議
DHCP: 動態主機配置協議
BOOTP: 啟動協議(用於無盤設備啟動)
DNS: 域名解析協議
TCP協議
TCP(全雙工,是一個比較復雜的協議 )協議特點:
1.有連接:需要用端口號建立連接
2.可靠傳輸:發送者能感知到是否發送成功
3.面向字節流:字節流讀取數據
TCP最核心的機制 :
1.可靠傳輸
2.盡可能提高傳輸效率
可靠性傳輸 發送者能感知到失敗(對比打電話和發短信)
面向字節流,文件操作(I/O流)
全雙工:既能發送也能接受
UDP雖不可靠,但傳輸效率比TCP高
TCP通過判斷發送的消息是否被回復,來判斷是否可靠傳輸
一、確認應答(可靠性的核心機制)
序號來說,按照每個字節的方式來編號的.
確認序列來說,表示當前序號之前的數據已經正確收到了
接下來對端應該給我發送確認序號開始的數據
二、超時重傳(可靠性的核心機制)和確認應答相輔相成
如果對方沒有確認應答,此時隔一定的時間之后,就需要重復傳輸這樣的數據,
重傳是為了進一步降低丟包的可能性,重傳的間隔時間采用的是一種比較悲觀的態度,
等待的時間越來越長
如果達到一定次數之后對方還沒有響應,就斷開和對方的通信連接
如果是應答數據包丟失(ACK),同樣會超時重傳,此時就導致接收方收到了兩份相同的數據,TCP會跟據序號來自動去重
三、連接管理(也是可靠性的一部分)
建立連接的意義:
1.雙方先試探下對方是否適合和我通信
2.雙方可以協商一些重要數據,序號從幾開始
三次握手
三次握手中涉及到的狀態變化
1.LISTEN狀態(服務器):手機開機,並且信號良好
2.ESTABLISHED狀態(客戶端/服務器):電話撥通之后對方接聽了
第一次握手:建立連接時,客戶端發送syn包(syn=1)到服務器,客戶端進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(syn=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手
為什么要三次握手?
三次握手的目的是建立可靠的通信信道,說到通訊,簡單來說就是數據的發送與接收,而三次握手最主要的目的就是雙方確認自己與對方的發送與接收機能正常
四次揮手的過程:
雙各自向對方發送FIN,再各自向對方發送ACK
中間的兩次交互可能合並為一個(在特殊情況下僅僅是可能,所以有些情況下是三次揮手)
第一次揮手,client:我說完了
第二次揮手,server:我知道了(但此時server可能還有話要說)
第三次揮手,server:我也說完了
第四次揮手,client:我知道了,(雙方掛了電話)
為什么要四次揮手?
答:根本原因是,一方發送FIN只表示自己發完了所有要發的數據,但還允許對方繼續把沒發完的數據發過來。
舉個例子:
A和B打電話,通話即將結束后,A說“我沒啥要說的了”,B回答“我知道了”,但是B可能還會有要說的話,A不能要求B跟着自己的節奏結束通話,於是B可能又巴拉巴拉說了一通,最后B說“我說完了”,A回答“知道了”,這樣通話才算結束。
雙方各自向對方發送FIN,再各自向對方發送ACK
中間的兩次交互,可能合並成一個(四次揮手變為三次揮手)
四次揮手的狀態轉換:
1.CLOSE_WAIT:收到第一個FIN的一方進入CLOSE_WAIT,是為了等待代碼中調用 關閉 方法
如果你發現服務器上出現大量的CLOSE_WAIT狀態,意味着什么?意味着代碼有bug,忘記調用close方法了
2.TIME_WAIT:主動斷開鏈接的一方進入到TIME_WAIT狀態,存在的意義是一旦最后一個 ACK 丟包,還有機會進行重傳
TIME_WAIT 會存在一定的時間,在超出這個時間之后,TIME_WAIT狀態才會真正消失,釋放對應的鏈接
2MSL:
MSL表示互聯網上任何兩點之間傳輸數據的最大時間 1min
四、滑動窗口(提高傳輸效率)
窗口的意義是:不等待ACK的情況下最多發送多少數據
滑動的含義是:每次收到 ACK 數據的同時,就繼續往后發下一組數據
如果窗口越大,傳輸效率越高
窗口也不能無限大,如果窗口太大可能會影響到可靠性
滑動窗口中如果丟包,采用快速重傳的方式來進行重傳
快速重傳:快速重傳本質上就是超時重傳,只不過重傳的時候,沒有拖泥帶水,只重傳了真正丟包的數據
五、流量控制
窗口越大,傳輸效率越高,但是窗口也不能無限大
如果窗口太大,可能接收端,處理不過來
若果生產速度超過了消費速度,接受緩沖區的內容就會越積越多,達到一定程度,緩沖區滿了,此時再傳輸的數據就會丟包
使用 接受緩沖區空余空間的大小,用這個指標衡量接收端的處理能力
通過這個指標來控制發送端的窗口大小(發送速度)
接收端緩沖區空余空間的大小,作為TCP協議報頭中的窗口大小,這個值就是發送端的滑動窗口大小的一個“建議值”
六、擁塞控制
滑動窗口的窗口大小不能無限大,即使接收端處理速度很快,也可能因為網路環境不佳導致數據丟包
最終的 滑動窗口大小是由 流量控制 和 擁塞控制 共同決定的
擁塞窗口:擁塞控制機制所建議的窗口大小,從一個比較小的數字開始,如果網絡通暢,放大窗口大小,如果網絡丟包,
縮小窗口大小
滑動窗口的最終值就是:流量控制的窗口 和 擁塞窗口 的較小值
慢開始:剛開始傳輸的時候擁塞窗口設置的小一些
七、延時應答(提高傳輸效率)
在可靠性的基礎上,盡量提高窗口大小
也是和滑動窗口以及流量控制相關
流量控制中需要在ACK中反饋接受緩沖區剩余空間的大小
此時采取的策略是,收到數據不立刻返回ACK,而是等一會,
等的過程中,程序就能多消費一些緩沖區中的數據,從而導致反饋的窗口大小就要更大一些
八、捎帶應答(提高傳輸效率)
建立在延時應答的基礎上的
內核反饋ACK的實際和程序反饋響應的時機合二為一,通過同一個數據報同時帶上兩方面的信息
九、面向字節流
粘包問題
由於面向字節流讀取數據方式沒有具體的約定,很難從接受緩存區中直接獲取到一個完整的應用層數據報
解決粘包問題只能從應用層角度入手,只要在應用層協議設定的時候,明確包的邊界就可以了
1、指定分割符
2、指定包的長度
十、異常情況
1.程序異常結束(沒啥影響,四次揮手正常完成)
2.系統關機(沒啥影響,本質上就是要先強制關閉所有程序)
3.主機掉電(拔網線)
a.掉電的是接收方,發送方會觸發超時重傳,嘗試重新建立鏈接,徹底釋放鏈接
b.掉電的是發送方,接收方如果一直接收不到數據的話,達到一定的時間,就會給對方
發送一個“心跳包”,如果沒有心跳了,就會重新嘗試鏈接,如果鏈接建立失敗了,徹底釋放鏈接
TCP和UDP的對比:
1.如果需要使用可靠傳輸,優先考慮TCP
2.如果傳輸的單個數據報比較大,還是考慮TCP
3.如果對可靠性要求沒那么高,但是對性能要求很高,優先考慮UDP
4.如果需要實現廣播,優先考慮UDP
如何使用UDP來進行可靠傳輸?
只能在應用層,通過代碼來手動實現