計算機網路 | 傳輸層篇 | 03
傳輸層篇 | 03
如果從資訊的處理角度來看,主要使用應用層提供通訊服務的。我們平時對網路進行程式設計的時候,很多時候都是直接對接傳輸層(使用傳輸層的介面來進行網路的程式設計)。因此我們說傳輸層是使用者功能的最底層,但是屬於面向通訊部分的最高層。
傳輸層的工作位置在計算機中。
對於網路中的路由器,並沒有傳輸層在工作。也就是在通訊的時候,傳輸層是工作在終端的裝置。
傳輸層的作用:管理端到端的通訊連線。
網路層的作用就是解決好虛擬的網際網路絡如何處理資料的路由,決定資料的走向。
網路層已經解決好了資料走向問題,那麼在傳輸層就不關心了,重點關心的是兩個端之間是如何進行通訊的。
傳輸層的作用就是讓兩個程序建立可以在網路中建立通訊。
作業系統中講解的程序之間的通訊是本機之間的程序通訊,不能跨網路,跨裝置。
計算機網路中將的程序通訊是可以跨網路,跨裝置的。
在計算機網路的傳輸層中需要引入埠的概念,用來標記不同的網路程序。
埠使用16bit表示(0~65535)
傳輸層主要就是學習兩個協議: TCP & UDP
UDP協議詳解
UDP相對於TCP來說是一個簡單的協議。
UDP中比較重要的概念是資料表(Datagram)。資料報是UDP協議的一個重要特徵。
資料報:應用層傳輸過來的完整資料。
UDP的資料長度主要由應用層傳輸的資料長度決定的,應用層傳輸的資料長度長,那麼UDP的資料就長(應該僅僅加個UDP頭部,不做任何其他處理)。
可以看出在各個網路層資料報的形式。
上圖就是UDP資料報的格式。
可以看到,UDP的頭部由四部分組成,將其和IP頭部比對,UDP是一個簡單的頭部。
由於UDP的頭部很簡單,也沒有辦法保證資料會一定傳送到,而且就算資料丟失了,也沒有辦法感知到。
UDP是面向報文傳輸的,它不會對報文進行任何的處理。而是直接塞進UDP的資料中,然後就傳送出去了。
與之對應的TCP,這個後面會講解。
上面就是UDP的特點
- UDP是無連線的
- UDP不能保證可靠的交付
- UDP是面向報文傳輸的
- UDP沒有擁塞控制
- UDP的首部開銷很小
TCP協議詳解
位元組流:流入程序或流出程序的位元組序列。
從應用層傳輸下來的資料,UDP將其看成是一塊資料,而TCP不見其看成是一塊資料,而是會將其拆分,看作是一系列的位元組流。TCP不是面向一整塊資料(面向資料報)而是面向一個位元組一個位元組來處理的。
所以在TCP中,可能先取出資料的某一段來進行傳輸,然後剩下的資料在放到第二個TCP報文來進行傳輸。
TCP會對使用者的資料進行拆分和合並操作,以保證資料可以更好的傳輸出去。
上面就是TCP的五個特點(對比UDP來寫)
- TCP是有連線的
- TCP提供可靠的交付
- TCP是面向位元組流的
- TCP有擁塞控制
- TCP首部開銷比較大
- TCP是全雙工通訊
除了可選的,TCP頭部的固定長度是20位元組。
TCP給每一個位元組都編號了,然後序號這裡儲存的就是這次TCP報文第一位元組的序號。
例子:
計算機收到的TCP報文的序號是501,然後資料的長度有100個位元組。計算機收到資料後,也就是501~600的位元組都收到了,那麼期望下一個傳遞迴來的序列號是601。
確認號需要配合序號一起使用。
結論:確認號為N,則表示N-1序號的資料都收到
資料偏移:真實的TCP資料偏離首部的距離
通過資料偏移,可以計算出TCP頭部的長度範圍,最少為20,4*15=60,所以TCP頭部的長度為[20,60]字。
需要有資料偏移是因為有TCP選項這一塊內容,所以要有資料偏移來記錄TCP頭部的長度。
TCP標記是非常有用的, 包含後面的三次握手,四次揮手都要不斷用到這裡的TCP標記。
視窗指明允許對方傳送的資料量,如果視窗的值為1000,那麼就表示允許對方傳送過來1000個位元組。
視窗可以和確認好結合運算,比如確認號是501,視窗的值是1000,那麼表示501~1500這麼多個位元組的資料都是可以接收的。
可靠傳輸協議的基本原理
可靠傳輸的基本原理,這裡瞭解了兩個協議:停止等待協議 & 連續ARQ協議
停止等待協議的核心是停止和等待。
連續ARQ協議的核心是滑動視窗和累計確認。
停止等待協議就如上圖, 接收方和傳送方一直處於停止等待,停止等待的過程。
第一種差錯情況: 傳送方沒有收到確認訊息,可能是傳送方發出去的訊息丟失了,那麼傳送方就會重新發送資料。
第二種差錯情況:確認訊息丟失,傳送方沒有收到確認訊息,會超時重傳。
第三種差錯情況:確認訊息沒有丟失,但是遲到了。
停止等待協議的三種可能查重情況
- 傳送訊息在路上丟失
- 確認訊息在路上丟失
- 確認訊息很久才到
TCP中一共有四個定時器,這裡學習第一個定時器——超時定時器。
這裡不再一個一個位元組傳送,而是直接把視窗中的6個位元組一起傳送。
但是如果要一個位元組一個位元組確認的話,那麼相當於是沒有改進的。
例如上圖,如果第5個位元組收到了確認了,那麼就表明序號5和之前的位元組都收到了(這是確認號的作用)。
然後就可以直接向後推進,這樣就省了很多次重複確認。
視窗控制與重發控制
-
確認報文丟失,這個其實是不要津的,可以通過後面的確認號來進行確認判斷。
-
傳送的報文丟失,那麼接收方就會重複傳送幾次同樣的一個確認號,接收方連續3次收到同一個確認號,就會將所對應的資料進行重發(快速重傳)。
TCP協議的可靠傳輸
TCP的可靠傳輸是基於連續ARQ的。
選擇重傳重傳的是一個邊界,也就是一個範圍,而不是某一個位元組。
網路的速度不一定是按序的!如果先收到了25和27的確認應答,但是沒有收到23,24的話,也要從23開始重傳。
選擇重傳:TCP可以選擇一些訊息重新傳輸,而不是把所有訊息重新傳輸。
(如果是按序收到的,那麼就是連續ARQ協議,如果不是按序收到的,就可以選擇重傳!)
選擇重傳的重傳序號存在TCP選項中,最多可以存放10個序號。
TCP協議的流量控制
流量控制是TCP協議的一個特有功能。
流量控制: 接收方通過控制視窗的大小,來約束髮送方的傳送速率
TCP協議的擁塞控制
流量控制和擁塞控制的差別。一個是考慮端到端的流量控制,一個是對網路全域性的考慮。
擁塞控制主要有兩個演算法:慢啟動演算法 & 擁塞避免演算法
慢啟動先指數增長,增長到慢啟動閾值就會停止,切換到擁塞避免演算法。
網路擁塞的意思就是傳輸報文不會發生超時。
慢啟動閾值一般都是告訴你的(怎麼計算先不管了...)
上圖就是是先進行慢啟動,達到閾值之後在進行擁塞避免演算法,不斷去+1試探。
TCP連線的三次握手
上面就是上次握手的過程
-> SYN=1,seq=x
<- SYN=1,ACK=1,seq=y,ack=x+1
-> ACK=1,seq=x+1,ack=y+1
TCP的三次握手最主要是防止已過期的連線再次傳到被連線的主機。
上圖所示,如果傳送方的請求報文進行了超時重傳, 第二個報文先到了,並得到接收方迴應,如果是兩次握手的話,那麼接收方迴應時就建立了連線,傳送方收到應答報文也建立連線。
但是問題是,如果超時報文接收方迴應了,那麼傳送方收到這個應答報文,也會建立連線,那麼就會建立兩次連線,這是不對的。
為了避免上述問題,傳送方在收到接收方的應答後,可以建立連線,並再傳送一次確認報文,這樣後面收到的請求應答報文都是無效的。而接收方再收到第三次握手才開始建立連線。
TCP連線的四次揮手
為什麼接收方連續傳送兩次確認報文呢?因為結束連線是傳送方主動請求的,傳送方的資料是傳送完成了,但是接收方可能還有資料需要接收,所以第一次應答報文是:好的,我知道了,等我收完資料先。然後接收方收完資料後就會發送第二條應答報文。
傳送方收到第二次應答報文後,會進入一個等待時間2MSL(報文最長存活時間),防止應答報文沒有傳送出去,等待時間過了就進行關閉。
接收方收到傳送方的應答報文就關閉了。
一般主動釋放連線後,也是不能馬上覆用該埠的,就是因為有這個等待計時器的存在,要等待2MSL後才會釋放埠(一般是4分鐘左右)。
為什麼要設定2MSL?
- 最後一個報文沒有確認
- 確保傳送方的ACK可以到達接收方
- 2MSL時間內沒有收到,則接收方會重發
- 確保當前連線的所有報文都已經過期
套接字與套接字程式設計
服務端程式碼
import socket
def server():
# 建立套接字
s = socket.socket()
# 繫結套接字
host = 'localhost'
port = 6666
s.bind((host, port))
# 監聽套接字
s.listen(5)
# 接收&處理訊息
while True:
c, addr = s.accept()
print("Connect Addr: ", addr)
c.send(b"Welcome!")
c.close()
if __name__ == "__main__":
server()
客戶端程式碼
import socket
def client(i):
# 建立套接字
s = socket.socket()
# 連線套接字
host = 'localhost'
port = 6666
s.connect((host, port))
# 傳送資訊
print("Recv msg:%s, Clinet: %d" % (s.recv(1024), i))
s.close()
if __name__ == "__main__":
for i in range(10):
client(i)
網路套接字的話,不管是不是本機,都要完整的走完整個協議棧。
域套接字本適合本機的程序之間通訊,對系統資源消耗也小。