日期:2020年4月10日
作者:Commas
勵志:當你的才華撐不起你的野心的時候,你就該靜下心來好好學習!
註釋:您的每一個小小舉動,將會是技術人大家庭的福澤,非常感謝……
如果您覺得這篇博客對您有所幫助,那麼不妨給我點個贊;
如果您覺得這篇博客有哪裏寫的不好的地方,那麼不妨給我點個撥。
此篇主要講解的是TCP理論知識,後面附帶了一個Python的示例,
如果您想了解更多有關Python的知識,那麼請點《Python學習的奇妙之旅》
文章目錄
一、TCP的定義
TCP
是Transmission Contol Protocol
的縮寫,即傳輸控制協議,是一種面向連接的
、可靠的
、基於字節流的
傳輸層通信協議。
二、TCP的報文格式以及功能與服務特性
三、三次握手與四次揮手
(1)建立連接過程分析(三次握手)
(2)釋放連接過程分析(四次握手)
(3)發送窗口與接收窗口
四、TCP差錯控制機制
差錯控制總共有序號、確認序號、ACK和檢驗和這四個字段共同參與工作,
另外,以上ACK指Acknowledgement Number(確認序號),而非Acknowledge Character(確認字符)
(1)數據分段
TCP
是面向字節流的服務
,也就是應用層提供給傳輸層是一連串的字節流。傳輸層在發送端發送數據之前,會對這些字節流進行分段,按規則編號,封裝成一些列的報文;而在接收端接收數據之後,會將接收到的報文,按照約定的規則,還原成之前的一連串的字節流,提交給應用層。
如上圖所示,一個3000字節長度的字節流,傳輸層接收到應用層的字節流之後,將其分成3段,每段爲1000個字節,那麼這1000個字節加上首部,即形成了TCP報文
,圖中共有3個報文,分別爲①②③。在首部控制信息中有一個序號
,它表示報文序列當前的第一個字節序號,如②中的1001,表示第二段報文的第一個字節,是從字節流1001位置開始的,這樣方便了接收端無論哪一個報文接收先,最終都可以準確無誤的還原發送端發送的數據字節流。
(2)確認應答
確認應答有以下四種方式:
- 逐個確認:一發一收一確認;
- 累計確認:多發多收一確認;
- 時延重複確認:重複確認,直到接收到“沒有接收到的”TCP數據報爲止;
- 丟失重複確認:重複確認,直到接收到“沒有接收到的”TCP數據報爲止;
(3)重傳
TCP差錯控制機制
的本質就是出錯重傳
,檢測到錯誤的數據或者丟失的數據就需要重新傳輸,讓接收端可以收到正確且完整的數據,從而完成自己的使命。
發送端確認某個TCP報文出錯的依據有兩個,如下:
- 發送端設置重傳定時器,重傳定時器溢出,還沒有收到確認應該,視爲需要重傳;
- 連續接收到4個確認序號(ACK)相同的確認應答,視爲報文丟失,需要重傳;
五、TCP擁塞控制機制
TCP擁塞控制機制
的本質就是根據網絡的狀況
決定發送窗口的發送窗口值
,減少發送數據量,從而規避網絡擁塞。而發送窗口值有一個很重要的公式,如下:
發送窗口值 =min(CWND,接收端公告的窗口字段值)
,其中,CWND爲擁塞窗口。
從上面的公式,我們不難發現,發送端調整流量就是改變擁塞窗口CWND的值,那我們該何時改變,該如何改變呢?這是我們接下來要討論並解決的問題。
(1)何時改變CWND值
何時改變?很簡單,應該是網絡發生擁塞的時候,就是需要改變的時候。但是發送端是如何知道網絡擁塞的呢?這個就需要摸着石頭過河,具體流程如下:
(2)如何改變CWND值
既然TCP報文丟失程度各有不同,那麼我們就需要有不同流量調整策略
應對不同程度丟失報文的情況,具體策略有兩種,如下:
1) 慢啓動
- TCP連接剛建立時,發送1個(即:20)TCP報文;
- 收到確認應答後,發送2個(即:21)報文;
- 再收到確認應答後,發送4個(即:22)報文;
- 依次成倍增大,直至達到接收端公告的窗口值或發生報文丟失爲止;
假設擁塞點=16,慢啓動如下圖所示:
2)擁塞避免
擁塞避免是在超過慢啓動閾值時,不再採用2的指數增長方式,而是採用線性增長,逐步接近擁塞點,如下圖所示:
3)慢啓動和擁塞避免示例
- ①之前,TCP連接剛建立,【慢啓動】探測網絡狀態;
- ①處,到達擁塞點;
- ①②之間,網絡狀態良好;
- ②處,
重傳定時器溢出
,流量降至爲1; - ②③之間,採用【慢啓動】進行流量增長;
- ③處,慢啓動閾值(即當前擁塞窗口的一半);
- ③④,一旦超過慢啓動閾值,開始【擁塞避免】,線性增長,逐步接近擁塞點;
- ④處,
連續4次相同確認序號的確認應答
,流量降至爲當前流量的一半,14÷2=7; - ④之後,繼續採用【擁塞避免】線性增長,逐步接近擁塞點。
六、基於TCP的socket類
socket(中文名:套接字)
,是進程間通信的一種方式。我們只要地址(IP+Port),就可以完成同一個終端
或者不同終端
兩個進程之間的通信,其中:
- IP:是Internet Protocol(網際互連協議)的縮寫,IP地址全網統一編址,標識互聯網中
不同的終端
。換而言之,通過IP,我們可以精準定位對應的終端; - Port:端口號,是終端中統一編址,標識終端中
不同應用進程
。換而言之,通過Port,我們可以精準定位對應的進程;
接下來我們藉助python中的socket類,來實現兩個進程之間的通信……
首先,我們需要實現TCP服務端,文件名爲tcp_server.py,代碼如下:
import socket
# 服務端的地址(ip+port)
ip_port = ("127.0.0.1", 9000)
# 創建一個套接字
tcp_sk = socket.socket()
# 將該套接字綁定服務器的地址(IP+port)
tcp_sk.bind(ip_port)
# 監聽連接
tcp_sk.listen(1)
# 接受客戶的連接
conn, addr = tcp_sk.accept()
# 設置接收1024個字節,並接收客戶端的消息和服務端的地址(IP+port)
ret = conn.recv(1024).decode("utf-8")
print(ret)
# 服務器發送消息給客戶端(消息必須轉換成字節傳輸)
conn.send(b"Hello,I'm server,I am really glad to serve you!")
# 關閉客戶端連接(客戶端套接字)
conn.close()
# 關閉服務器套接字
tcp_sk.close()
接下來,我們需要實現TCP客戶端,文件名爲tcp_client.py,代碼如下:
import socket
# 服務端的地址(ip+port)
ip_port = ("127.0.0.1", 9000)
# 創建一個套接字
tcp_sk = socket.socket()
# 嘗試連接服務器
tcp_sk.connect(ip_port)
# 客戶端發送消息給服務端
tcp_sk.send(b"Hello,I'm client!")
# 設置接收1024個字節,並接收服務端的消息和服務端的地址(IP+port)
ret = tcp_sk.recv(1024)
print(ret.decode("utf-8"))
實現了TCP服務端和客戶端之後,我們首先要啓動服務端(即運行tcp_server.py文件),服務端在等待客戶端連接,所以控制檯並無消息輸出。
然後再運行客戶端(即運行tcp_client.py文件),控制檯順出結果如下(按先後次序):
- 服務端控制檯:
Hello,I'm client!
- 客戶端控制檯:
Hello,I'm server,I am really glad to serve you!
版權聲明:本文爲博主原創文章,如需轉載,請給出:
原文鏈接:https://blog.csdn.net/qq_35844043/article/details/105385186