python網路-Socket之TCP程式設計(26)

Se7eN_HOU發表於2019-05-22

一、TCP簡介

1、TCP介紹

TCP協議,傳輸控制協議(英語:Transmission Control Protocol,縮寫為 TCP)是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議。

TCP通訊需要經過建立連線、資料傳送、終止連線三個步驟。

TCP通訊模型中,在通訊開始之前,一定要先建立相關的連結,才能傳送資料,類似於生活中,"打電話"。

2、TCP面向連線

通訊雙方必須先建立連線才能進行資料的傳輸,雙方都必須為該連線分配必要的系統核心資源,以管理連線的狀態和連線上的傳輸。

雙方間的資料傳輸都可以通過這一個連線進行。

完成資料交換後,雙方必須斷開此連線,以釋放系統資源。

這種連線是一對一的,因此TCP不適用於廣播的應用程式,基於廣播的應用程式請使用UDP協議。

3、TCP可靠傳輸

1)TCP採用傳送應答機制

TCP傳送的每個報文段都必須得到接收方的應答才認為這個TCP報文段傳輸成功

2)超時重傳

傳送端發出一個報文段之後就啟動定時器,如果在定時時間內沒有收到應答就重新傳送這個報文段。TCP為了保證不發生丟包,就給每個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。然後接收端實體對已成功收到的包發回一個相應的確認(ACK);如果傳送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的資料包就被假設為已丟失將會被進行重傳。

3)錯誤校驗

TCP用一個校驗和函式來檢驗資料是否有錯誤;在傳送和接收時都要計算校驗和。

4) 流量控制和阻塞管理

流量控制用來避免主機傳送得過快而使接收方來不及完全收下。

4、TCP與UDP的不同點

  • 面向連線(確認有建立三方交握,連線已建立才作傳輸。)
  • 有序資料傳輸
  • 重發丟失的資料包
  • 捨棄重複的資料包
  • 無差錯的資料傳輸
  • 阻塞/流量控制

 

二、TCP資料包格式

所謂三次握手(Three-way Handshake),是指建立一個TCP連線時,需要客戶端和伺服器總共傳送3個資料包

那麼我們就先來看一下TCP資料包的格式:

 

在TCP層,有個FLAGS欄位,這個欄位有以下幾個標識:SYN, FIN, ACK, PSH, RST, URG.
  • URG—為1表示高優先順序資料包,緊急指標欄位有效。
  • ACK—為1表示確認號欄位有效
  • PSH—為1表示是帶有PUSH標誌的資料,指示接收方應該儘快將這個報文段交給應用層而不用等待緩衝區裝滿。
  • RST—為1表示出現嚴重差錯。可能需要重現建立TCP連線。還可以用於拒絕非法的報文段和拒絕連線請求。
  • SYN—為1表示這是連線請求或是連線接受請求,用於建立連線和使順序號同步
  • FIN—為1表示傳送方沒有資料要傳輸了,要求釋放連線
  • Seq---序號,這是為了連線以後傳送資料用的,
  • Ack---確認號對收到的資料包的確認,值是等待接收的資料包的序列號+1。

 

三、TCP的三次握手

三次握手的目的是連線伺服器指定埠,建立TCP連線,並同步連線雙方的序列號和確認號並交換 TCP 視窗大小資訊.在socket程式設計中,客戶端執行connect()時。將觸發三次握手。

三次握手示意圖

第一次握手:(Client向Server傳送聯機請求)

SYN=1(Client向Server傳送聯機請求)

Client想要與Server進行TCP通訊,首先他需要向Server傳送一個SYN=1的同步序列編號(syncsynchronized squsequence number)用來表示建立連線,並且隨機產生一個數Seq number = X的資料包到Server,Server由於SYN=1知道,Client要求建立聯機,到這裡第一次握手就結束了

第二次握手:(Server向Client回覆聯機並確認聯機資訊)

SYN=1(Server接受Client的聯機請求)

ACK=1(確認資訊)

這是對第一次握手資訊的確認,表示Server收到了Client的第一次握手資訊

Ack=X+1(確認回覆)

同時Server回覆Client一個確認碼Ack表示你的聯機請求我已經收到,而且資料沒有丟失,怎麼驗證資料沒有丟失呢?即Ack的值等於Client發過來Seq的值加1,即Ack = X+1。因為我都知道你發過來的Seq的值,所以這個資料包沒有丟失。

Seq = Y(第二次握手的資料包序列號)

Server給Client的資料包序列號,為了資料包在到達Client之後的驗證,所以這次從Server到Client的資料包中同樣也會隨機產生一個Seq number = Y,

第三次握手

ACK=1(對第二次握手的確認)

首先Client會開啟Server傳送過來的Ack驗證一下是否正確為Seq+1,即第一次傳送的seq number+1,確認無誤後,Client仍然需要給Server再次回覆確認即ACK=1

Seq=Z(第三次握手的資料包序列號)

Ack=Y+1

Client告訴Server,你給我回復的資訊我也收到了,怎麼確定我收到了你的資訊呢?就是通過Ack等於第二次握手傳遞過來的Seq值+1。到此為止三次握手結束進入ESTABLISHED狀態,開始進行資料傳輸。

 

四、TCP四次揮手

 

第一次揮手傳送FIN請求,第一次揮手結束。

第二次揮手開始,被動方向主動方傳送ACK確認碼,到這裡第二次揮手結束。

第三次握手開始被動方向主動方傳送FIN號結束。

第四次揮手開始主動方向被動方傳送ACK確認,等待2MSL後斷開TCP連線。

 

五、TCP的十種狀態

這十種狀態分別是三次握手和四次揮手中的狀態,在上面兩個圖中都給大家標記出來了,這裡再給大家一個簡單的圖表示

 

六、TCP的2MSL問題

在四次揮手中我們提到了時間等待狀態,等待的時間是2MSL。

2MSL即兩倍的MSL,TCP的TIME_WAIT狀態也稱為2MSL等待狀態,

當TCP的一端發起主動關閉,在發出最後一個ACK包後,即第3次揮手完成後傳送了第四次揮手的ACK包後就進入了TIME_WAIT狀態,必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要目的是怕最後一個 ACK包對方沒收到,那麼對方在超時後將重發第三次揮手的FIN包,主動關閉端接到重發的FIN包後可以再發一個ACK應答包。在TIME_WAIT狀態時兩端的埠不能使用,要等到2MSL時間結束才可繼續使用。當連線處於2MSL等待階段時任何遲到的報文段都將被丟棄。不過在實際應用中可以通過設定 SO_REUSEADDR選項達到不必等待2MSL時間結束再使用此埠。

 

七、TCP長連線和短連線

TCP在真正的讀寫操作之前,server與client之間必須建立一個連線,

當讀寫操作完成後,雙方不再需要這個連線時它們可以釋放這個連線,

連線的建立通過三次握手,釋放則需要四次握手,

所以說每個連線的建立都是需要資源消耗和時間消耗的。

1. TCP短連線

模擬一種TCP短連線的情況:

  1. client 向 server 發起連線請求
  2. server 接到請求,雙方建立連線
  3. client 向 server 傳送訊息
  4. server 回應 client
  5. 一次讀寫完成,此時雙方任何一個都可以發起 close 操作

在第 步驟5中,一般都是 client 先發起 close 操作。當然也不排除有特殊的情況。從上面的描述看,短連線一般只會在 client/server 間傳遞一次讀寫操作!

2. TCP長連線

再模擬一種長連線的情況:

  1. client 向 server 發起連線
  2. server 接到請求,雙方建立連線
  3. client 向 server 傳送訊息
  4. server 回應 client
  5. 一次讀寫完成,連線不關閉
  6. 後續讀寫操作...
  7. 長時間操作之後client發起關閉請求

3. TCP長/短連線操作過程

(1)短連線的操作步驟是:建立連線——資料傳輸——關閉連線...建立連線——資料傳輸——關閉連線

(2) 長連線的操作步驟是:建立連線——資料傳輸...(保持連線)...資料傳輸——關閉連線

4. TCP長/短連線的優點和缺點

  • 長連線可以省去較多的TCP建立和關閉的操作,減少浪費,節約時間。對於頻繁請求資源的客戶來說,較適用長連線。

  • client與server之間的連線如果一直不關閉的話,會存在一個問題,隨著客戶端連線越來越多,server早晚有扛不住的時候,這時候server端需要採取一些策略,如關閉一些長時間沒有讀寫事件發生的連線,這樣可以避免一些惡意連線導致server端服務受損;如果條件再允許就可以以客戶端機器為顆粒度,限制每個客戶端的最大長連線數,這樣可以完全避免某個蛋疼的客戶端連累後端服務。

  • 短連線對於伺服器來說管理較為簡單,存在的連線都是有用的連線,不需要額外的控制手段。但如果客戶請求頻繁,將在TCP的建立和關閉操作上浪費時間和頻寬。

5. TCP長/短連線的應用場景

  • 長連線多用於操作頻繁,點對點的通訊,而且連線數不能太多情況。每個TCP連線都需要三次握手,這需要時間,如果每個操作都是先連線,再操作的話那麼處理速度會降低很多,所以每個操作完後都不斷開,再次處理時直接傳送資料包就OK了,不用建立TCP連線。

    例如:資料庫的連線用長連線,如果用短連線頻繁的通訊會造成socket錯誤,而且頻繁的socket 建立也是對資源的浪費。

  • 而像WEB網站的http服務一般都用短連結,因為長連線對於服務端來說會耗費一定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的連線用短連線會更省一些資源,如果用長連線,而且同時有成千上萬的使用者,如果每個使用者都佔用一個連線的話,那可想而知吧。所以併發量大,但每個使用者無需頻繁操作情況下需用短連好。

 

八、TCP的通訊模型

tcp通訊模型中,在通訊開始之前,一定要先建立相關的連結,才能傳送資料,類似於生活中,"打電話"

生活中的電話機,如果想讓別人能更夠打通我們們的電話獲取相應服務的話,需要做一下幾件事情:

  1. 買個手機
  2. 插上手機卡
  3. 設計手機為正常接聽狀態(即能夠響鈴)
  4. 靜靜的等著別人撥打

tcp伺服器如同上面的電話機過程一樣,在程式中,如果想要完成一個tcp伺服器的功能,需要的流程如下:

  1. 建立一個socket套接字
  2. bind繫結ip和port
  3. listen使套接字變為可以被動連結
  4. accept等待客戶端的連結
  5. recv/send接收傳送資料

 

九、TCP伺服器程式碼實現

#coding = utf-8
from socket import *
#1、建立socket套接字
tcpServerSocket = socket(AF_INET,SOCK_STREAM)
#2、繫結本地資訊
address = ("",7788)
tcpServerSocket.bind(address)
#3、使用socket建立的套接字預設的屬性是主動的,使用listen將其變為被動,這樣就可以等著別人連結了
tcpServerSocket.listen(5)

"""
如果有新的客戶端來連結伺服器,那麼就產生一個新的套接字專門為這個客戶端伺服器
newSocket用來為這個客戶端服務
tcpServerSocket就可以省下來專門等待其他的客戶端的連結
"""
newSocket,clientAddress = tcpServerSocket.accept()

#4、接收物件傳送過來的資料,最大接收1024個位元組
reveiveData = newSocket.recv(1024)
print("接收的資料為:%s"%reveiveData.decode())

#5、傳送資料到客戶端
newSocket.send("haha".encode())

#6、關閉為這個客戶端服務的套接字
newSocket.close()

#7、關閉監聽套接字
tcpServerSocket.close()

執行流程

1、TCP伺服器

2、網路除錯助手:

 

十、TCP客戶端程式碼實現

所謂的伺服器端:就是提供服務的一方,而客戶端,就是需要被服務的一方

tcp的客戶端要比伺服器端簡單很多,如果說伺服器端是需要自己買手機、查手機卡、設定鈴聲、等待別人打電話流程的話,那麼客戶端就只需要找一個電話亭,拿起電話撥打即可,流程要少很多

#coding = utf-8
from socket import *

#1、建立socket
tcpClientSocket = socket(AF_INET,SOCK_STREAM)

#2、連結伺服器
serverAddress = ("192.168.100.106",7788)
tcpClientSocket.connect(serverAddress)

#3、向伺服器傳送資料
tcpClientSocket.send("哈哈".encode("gb2312"))

#4、接收對方傳送過來的資料,最大接收1024個位元組
receiveData = tcpClientSocket.recv(1024)
print("接收到的資料為%s"%receiveData.decode("gb2312"))

#5、關閉套接字
tcpClientSocket.close()

執行流程:

1、tcp客戶端

2、網路除錯助手:

 

相關文章