溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python中TCP協(xié)議的三次握手與四次揮手是什么

發(fā)布時間:2021-11-20 16:44:45 來源:億速云 閱讀:102 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“Python中TCP協(xié)議的三次握手與四次揮手是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

一、TCP、UDP 協(xié)議的區(qū)別

在介紹這兩者的區(qū)別之前,我們要需要了解一個概念:TCP/IP 協(xié)議族。定義如下:

目前 Internet(因特網(wǎng))使用的主流協(xié)議族是 TCP/IP 協(xié)議族,它是一個分層、多協(xié)議的通信體系 《Linux高性能編程》

提取關(guān)鍵詞:分層、多協(xié)議和通信。也就是說,它有多個層次,每個層次有不同的協(xié)議,這些層次之間通過協(xié)議相互協(xié)作,最終達到網(wǎng)絡(luò)通信的目的。

說到分層,應(yīng)該不會很陌生,TCP/IP 協(xié)議族是一個四層協(xié)議系統(tǒng),自底而上分別是:數(shù)據(jù)鏈接層、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層。我們這里要說到的 TCP 和 UDP 協(xié)議屬于傳輸層。(各層的作用及相關(guān)協(xié)議這里暫時先不做介紹)

下面我們回到標(biāo)題:TCP、UDP 協(xié)議的區(qū)別,總結(jié)起來這個問題的答案要點如下:

1、首頁它們倆都是傳輸層的協(xié)議,而所謂“傳輸層”,它是為兩臺主機提供端到端的通信,即從 A <-> B 。

2、TCP 協(xié)議可靠,UDP 協(xié)議不可靠??煽考粗笖?shù)據(jù)由 A 發(fā)送到 B,是否能確保數(shù)據(jù)真的有送達到 B。TCP 協(xié)議使用 超時重傳、數(shù)據(jù)確認(rèn)等方式來確保數(shù)據(jù)包被正確地發(fā)送至目的端,而 UDP 協(xié)議無法保證數(shù)據(jù)從發(fā)送端正確傳送到目的端,如果數(shù)據(jù)在傳輸過程中丟失、或者目的端通過數(shù)據(jù)檢驗發(fā)現(xiàn)數(shù)據(jù)錯誤,則 UDP 協(xié)議只是簡單地通知應(yīng)用程序發(fā)送失敗,對于 TCP 協(xié)議擁有的超時重傳、數(shù)據(jù)確認(rèn)等需要應(yīng)用程序自己來處理這些邏輯。

3、TCP 是面向連接的,UDP 是無連接的。這也比較好理解,因為 TCP 連接才需要“三次握手,四次揮手”。

4、TCP 服務(wù)是基于流的,而 UDP 是基于數(shù)據(jù)報的,基于流的數(shù)據(jù)沒有邊界(長度)限制,而基于數(shù)據(jù)報的服務(wù),每個 UDP 數(shù)據(jù)報都有一個長度,接收端必須以該長度為最小單位將其所有內(nèi)容一次性讀出。

5、當(dāng)發(fā)送方多次執(zhí)行寫操作時,TCP 模塊會先將這些數(shù)據(jù)放入 TCP 發(fā)送緩沖區(qū)中,當(dāng) TCP 模塊真正開始發(fā)送數(shù)據(jù)時,發(fā)送緩沖區(qū)中這些等待發(fā)送的數(shù)據(jù)可能被封裝成一個或多個 TCP 報文段發(fā)出,因此,TCP 模塊發(fā)出的 TCP 報文段的個數(shù)與應(yīng)用程序執(zhí)行的寫操作次數(shù)是沒有固定數(shù)量關(guān)系的。同樣,當(dāng)接收端收到一個或多個 TCP 報文段后,TCP 模塊將這些數(shù)據(jù)按照序號(序號說明見下面 的 TCP 頭部結(jié)構(gòu))依次放入 TCP 接收緩沖區(qū)中,并通知應(yīng)用程序讀取數(shù)據(jù)。接收端可選擇一次或者分多次將數(shù)據(jù)從緩沖區(qū)中讀出(這取決于用戶指定的應(yīng)用程序讀緩沖區(qū)的大?。?。因此,接收端讀取數(shù)據(jù)的次數(shù)與發(fā)送端發(fā)出多少個報文段個數(shù)也沒有固定的數(shù)量關(guān)系??偨Y(jié)來說,即**對于 TCP 連接,發(fā)送端執(zhí)行的寫操作次數(shù)與接收端執(zhí)行的讀操作次數(shù)之間沒有任何數(shù)據(jù)關(guān)系,這也是基于流服務(wù)的特點。**而對于 UDP 服務(wù),發(fā)送端每執(zhí)行一次寫操作,就會將其封裝成一個 UDP 數(shù)據(jù)報并發(fā)送之,同時接收端必須根據(jù)發(fā)送的進行讀,否則就會丟包。因此,對于 UDP 連接,發(fā)送端寫的次數(shù)據(jù)與讀的次數(shù)是一致的,這也是基于數(shù)據(jù)報的服務(wù)的特點。

6、TCP 的連接是一對一的,所以如果是基于廣播或者多播的的應(yīng)用程序不能使用 TCP,而 UDP 則非常適合廣播和多播。

總結(jié)一句定義:

TCP 協(xié)議(Transmission Control Protocal,傳輸控制協(xié)議)為應(yīng)用層提供可靠的、面向連接的、基于流的服務(wù)。而 UDP 協(xié)議(User Datagram Protocal,用戶數(shù)據(jù)報協(xié)議)則與 TCP 協(xié)議完全相反,它為應(yīng)用層提供不可靠、無連接和基于數(shù)據(jù)報的服務(wù)。

二、TCP 頭部結(jié)構(gòu)

TCP 報文結(jié)構(gòu)分為頭部部分和數(shù)據(jù)部分,為什么需要了解 TCP 頭部結(jié)構(gòu),因為在后面“三次握手與四次揮手”里會用到頭部結(jié)構(gòu)里的標(biāo)志位。簡單了解下對理解后面的過程有一定的好處。

下面一一說明每個的作用:

16位源端口號與目的端口號,這個比較好理解,就不過多解釋。

32位序號:在建立連接(或者關(guān)閉)的過程,這個序號是用來做占位,當(dāng) A 發(fā)送連接請求到 B,這個時候會帶上一個序號(隨機值,稱為 ISN),而 B 確認(rèn)連接后,會把這個序號 +1 返回,同時帶上自己的充號。當(dāng)建立連接后,該序號為生成的隨機值 ISN 加上該段報文段所攜帶的數(shù)據(jù)的第一個字節(jié)在整個字節(jié)流中的偏移量。比如,某個 TCP 報文段發(fā)送的數(shù)據(jù)是字節(jié)流中的第 100 ~ 200 字節(jié),那該序號為 ISN + 100。所以總結(jié)起來說明建立連接(或者關(guān)閉)時,序號的作用是為了占位,而連接后,是為了標(biāo)記當(dāng)前數(shù)據(jù)流的第一個字節(jié)。

4位頭部長度:標(biāo)識 TCP 頭部有多少個 32 bit 字,因為是 4位,即 TCP 頭部最大能表示 15,即最長是 60 字節(jié)。即它是用來記錄頭部的最大長度。

6位標(biāo)志位,包括:

URG 標(biāo)志:表示緊急指針是否有效。

ACK 標(biāo)志:確認(rèn)標(biāo)志。通常稱攜帶 ACK 標(biāo)志的 TCP 報文段為確認(rèn)報文段。

PSH 標(biāo)志:提示接收端應(yīng)該程序應(yīng)該立即從 TCP 接收緩沖區(qū)中讀走數(shù)據(jù),為接收后續(xù)數(shù)據(jù)騰出空間(如果不讀走,數(shù)據(jù)就會一直在緩沖區(qū)內(nèi))。

RST 標(biāo)志:表示要求對方重新建立連接。通常稱攜帶 RST 標(biāo)志的 TCP 報文段為復(fù)位報文段。

SYN 標(biāo)志:表示請求建立一個連接。通常稱攜帶 SYN 標(biāo)志的 TCP 報文段稱為同步報文段。

FIN 標(biāo)志:關(guān)閉標(biāo)志,通常稱攜帶 FIN 標(biāo)志的 TCP 報文段為結(jié)束報文段。

這些標(biāo)志位說明了當(dāng)前請求的目的,即要干什么。

16 位窗口大?。罕硎井?dāng)前 TCP 接收緩沖區(qū)還能容納多少字節(jié)的數(shù)據(jù),這樣發(fā)送方就可以控制發(fā)送數(shù)據(jù)的速度,它是 TCP 流量控制的一個手段。

16 位校驗和:驗證數(shù)據(jù)是否損壞,通過 CRC 算法檢驗。這個校驗不僅包括 TCP 頭部,也包括數(shù)據(jù)部分。

16 位緊急指針:正的偏移量,它和序號字段的值相加表示最后一個緊急數(shù)據(jù)的下一字節(jié)的序號。TCP 的緊急指針是發(fā)送端向接收端發(fā)送緊急數(shù)據(jù)的方法。

TCP 頭部選項:可變長的可選信息,這部分最多包含 40 字節(jié),因為 TCP 頭部最長是 60 字節(jié),所以固定部分占 20 字節(jié)。這里不做詳細介紹,可以參考《Linux高性能編程》3.2.2

三、三次握手與四次揮手過程詳解

畫圖

先來解釋三次握手過程:

1、發(fā)送端發(fā)送連接請求,6位標(biāo)志為 SYN,同時帶上自己的序號(此時由于不傳輸數(shù)據(jù),所以不表示字節(jié)的偏移量,只是占位),比如是 223。

2、接收端接到請求,表示同意連接,發(fā)送同意響應(yīng),帶上 SYN + ACK 標(biāo)志位,同時將確認(rèn)序號為 224(發(fā)送端序號加1),并帶上自己的序號(此時同樣由于不傳輸數(shù)據(jù),所以不表示字節(jié)的偏移量,只是占位),比如是 521。

3、發(fā)送端接收到確認(rèn)信息,再發(fā)回給接收端,表示我已接受到你的確認(rèn)信息,此時標(biāo)志仍為 ACK,確認(rèn)序號為 522。

涉及到的問題:為什么是三次握手,而不是四次或者兩次?

首先解釋為什么不是四次。四次的過程是這樣的:

發(fā)送方:我要連你了。

接收方:好的。

接收方:我準(zhǔn)備好了,你連吧。

發(fā)送方:好的。

顯然接收方準(zhǔn)備好連接并同意連接是可以合并的,這樣可以提高連接的效率。

再來,我們解釋為什么不是兩次。其實也比較好理解,我們知道 TCP 是全雙工通信的,同時也是可靠的,連接和關(guān)閉都是兩邊都要執(zhí)行才算真正的完成,同時還需要確保兩端都已經(jīng)執(zhí)行了連接或者關(guān)閉。如果只有兩次,過程是這樣的:

發(fā)送方:我要連你了。

接收方:好的。

很明顯,接收方并不知道也不能保證發(fā)送方一定接收到 “好的” 這條信息,一旦接收方真的沒有收到這條信息,就會出現(xiàn)接收收“單方面連接”的情況,這個時候發(fā)送方就會一直重試發(fā)送連接請求,直到真正收到 “好的” 這條信息之后才算連接完成。而對于三次,如果發(fā)送方?jīng)]有等待到你回復(fù)確認(rèn),它是不會真正處于連接狀態(tài)的,它會重試確認(rèn)請求。

接著我們來看看四次揮手過程:

1、發(fā)送方發(fā)送關(guān)閉請求,標(biāo)志位為:FIN,同時也會帶上自己的序號(此時同樣由于不傳輸數(shù)據(jù),所以不表示字節(jié)的偏移量,只是占位)。

2、接收方接到請求后,回復(fù)確認(rèn):ACK,同時確認(rèn)序號為請求序號加1。

3、接收方也決定關(guān)閉連接,發(fā)送關(guān)閉通知,標(biāo)志位為 FIN,同時還會帶上第2步中的確認(rèn)信息,即 ACK,以及確認(rèn)序號和自己的序號。

4、發(fā)送方回復(fù)確認(rèn)信息:ACK,接收方序號加1。

涉及到的問題:為什么需要四次握手,不是三次?

三次的過程是這樣的:

發(fā)送方:我不再給你發(fā)送數(shù)據(jù)了。

接收方:好的,我也不給你發(fā)了。

發(fā)送方:好的,拜拜。

這是因為當(dāng)接收方收到關(guān)閉請求后,它能立馬響應(yīng)的就是確認(rèn)關(guān)閉,它這里確認(rèn)的是接收方的關(guān)閉,即發(fā)送方不再發(fā)數(shù)據(jù)給接收方了,但他還是可以接收接收方發(fā)給他的數(shù)據(jù)。而接收方是否需要關(guān)閉“發(fā)送數(shù)據(jù)給發(fā)送方”這條通道,取決于操作系統(tǒng)。操作系統(tǒng)也有可能 sleep 個幾秒再關(guān)閉,如果合并成三次,就可能造成接收方不能及時收到確認(rèn)請求,可能造成超時重試等情況。因此需要四次。

四、什么是 TIME_WAIT 狀態(tài)

首先,我們來看一段代碼(說了這么多理論,終于要看點代碼了)。這里舉一個 python 簡單的使用 socket 進行 tcp 通信的示例:

服務(wù)端:

socket_server_test.py
# -*- coding: utf-8 -*-
"""
@Time : 2019/6/26 下午4:58
@Author : Demon
@File : socket_server_test.py
@Desc : 
"""
import socket
HOST = '127.0.0.1' # 標(biāo)準(zhǔn)的回環(huán)地址 (localhost)
PORT = 9999 # 監(jiān)聽的端口 (非系統(tǒng)級的端口: 大于 1023)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 第三個參數(shù),如果為0,也不可復(fù)用
# 第三個如果為1,可以復(fù)用
# s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
 print('Connected by', addr)
 while True:
 data = conn.recv(1024)
 print("data:", data)
 if data:
 print("close")
 s.close()
 break
 conn.sendall(data)

客戶端:

# -*- coding: utf-8 -*-
"""
@Time : 2019/6/26 下午4:55
@Author : yrr
@File : socket_client_test.py
@Desc : 測試 self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
"""
import socket
HOST = '127.0.0.1' # 服務(wù)器的主機名或者 IP 地址
PORT = 9999 # 服務(wù)器使用的端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))

執(zhí)行效果:

我們會發(fā)現(xiàn),當(dāng)我們服務(wù)端主動關(guān)閉時,如果我們再次運行這個程序,會報錯誤說端口仍然被占用。這就很奇怪了,明明已經(jīng)關(guān)閉了連接,為什么還會占用著端口呢?我們使用 netstat -an|grep 9999 命令查看,發(fā)現(xiàn)當(dāng)前這個連接處于 TIME_WAIT 狀態(tài)。

我們來說下 TIME_WAIT 狀態(tài)。即當(dāng)一方斷開連接后,它并沒有直接進入 CLOSED 狀態(tài),而是轉(zhuǎn)移到 TIME_WAIT 狀態(tài),在這個狀態(tài),需要等待 2MSL(Maximum Segment Life,報文段最大生存時間)的時間,才能完全關(guān)閉。

涉及問題:

1、為什么需要有 TIME_WAIT 狀態(tài)存在?

簡單來說有兩點原因如下:

a. 當(dāng)最后發(fā)送方發(fā)出確認(rèn)信息后,仍然不能保證接收方能收到信息,萬一沒收到,那接收方就會重試,而此時發(fā)送方已經(jīng)真正關(guān)閉了,就接受不到請求了。

b. 如果發(fā)送方在發(fā)出確認(rèn)信息后就關(guān)閉了,在接收方接到確認(rèn)信息的過程中,發(fā)送方是有可能再次發(fā)出連接請求的,那這個時候就亂套了。剛連接完,又收到確認(rèn)關(guān)閉的信息。

2、為什么時長是 2MSL 呢?

這個其實也比較好理解,所以我發(fā)送確認(rèn)信息,到達最長時間是 MSL,而你如果沒接受到,再重試,時間最長也是 MSL,那我等 2MSL,如果還沒收到請求,證明你真的已經(jīng)正常收到了。

正因為我們有這個 TIME_WAIT 狀態(tài),所以通常我們說是客戶端先關(guān)閉,一般不會讓服務(wù)器端先關(guān)閉。那如何避免出現(xiàn)關(guān)閉后端口被占用的情況(即上面代碼示例問題怎么解決)呢?很簡單,加一行代碼即可實現(xiàn):

# socket.SO_REUSEADDR 表示 close 后端口可復(fù)用
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

“Python中TCP協(xié)議的三次握手與四次揮手是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI