初探TCP

QiShare發表於2019-01-21

級別: ★★☆☆☆
標籤:「TCP」「三次握手」「資料傳輸」「四次揮手」
作者: WYW
審校: QiShare團隊

筆者最近看了關於Python的部分內容,發現網路程式設計部分非常容易能夠建立一個本地TCP伺服器,正好可以用來分析一下TCP的請求和響應過程。 在本篇文章,筆者將給大家介紹下TCP建立連線(三次握手),傳輸資料,斷開連線(四次揮手)的過程。

TCP簡介

  • TCP:TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連線的可靠的基於位元組流傳輸層通訊協議,由IETF的RFC 793定義。

  • 聊到網路協議,我們常常會想到OSI(Open System Interconnection 開放式系統互聯)七層模型、TCP/IP協議簇,她位於OSI、TCP/IP協議簇哪一層等問題。 如下圖OSI七層模型及對應的TCP/IP協議簇所示,TCP位於OSI中的第四層(傳輸層)。位於TCP/IP協議簇中的第四層(TCP or UDP)。

下圖為OSI七層模型及對應的TCP/IP協議簇

OSI TCP/IP Family

  • TCP是面向連線的,是指客戶端在傳送、接收資料之前需要先建立連線,這個連線過程需要三次握手來完成,筆者藉助Python搭建了一個本地的TCP服務,並使用Wireshark(Wireshark(前稱Ethereal)是一個網路封包分析軟體。網路封包分析軟體的功能是擷取網路封包,並儘可能顯示出最為詳細的網路封包資料。 )捕獲了本地的TCP伺服器和TCP客戶端之間請求響應的過程,帶大家一起看一下建立連線(三次握手),傳輸資料,斷開連線(四次揮手)的過程。

本地搭建TCP服務準備工作:

筆者在前文提到了要用Python建立一個本地TCP伺服器,並且分析TCP的請求響應過程。這裡筆者使用的是PythonIDE、Mac自帶的終端簡單建立了一個本地TCP服務端和客戶端。

筆者會分析的過程如下:
-> 建立並且啟動一個埠號為20000的TCP服務端
-> 建立客戶端並和服務端建立連線(三次握手)
-> 客戶端向服務端傳送資料'AB'
-> 服務端到資料後給客戶端傳送資料'AB'
-> 服務端收到資料向客戶端傳送收到的資料(當前即'AB')
-> 客戶端和服務端斷開連線(四次揮手)
-> 使用Wireshark對建立連線(三次握手),傳輸資料,斷開連線(四次揮手)的過程進行分析
服務端程式碼:

Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 03:13:28) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license()" for more information.
>>> from socketserver import BaseRequestHandler, TCPServer
>>> class EchoHandler(BaseRequestHandler):
	def handle(self):
		print('Got connection from', self.client_address)
		while True:
			msg = self.request.recv(8192)
			if not msg:
				break
			self.request.send(msg)

			
>>> if __name__ == '__main__':
	serv = TCPServer(('', 20000), EchoHandler)
	serv.serve_forever()

	
Got connection from ('127.0.0.1', 59006)
複製程式碼

客戶端程式碼:

wangyongwangdeiMac:~ wangyongwang$ python
Python 2.7.15 (default, Oct  2 2018, 11:47:18) 
[GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from socket import socket, AF_INET, SOCK_STREAM
>>> s = socket(AF_INET, SOCK_STREAM)
>>> s.connect(('localhost', 20000))
複製程式碼

上述程式碼的效果如下圖所示:

TCP請求響應效果圖

TCP建立連線效果圖

如上圖,Got connection from ('127.0.0.1', 62515)我們可以確定客戶端使用的埠是59006

通過上述準備工作,下邊筆者會使用Wireshark捕獲TCP請求響應的整個過程,並進行相應分析。

TCP三次握手

  • TCP通過三次握手建立連線,我們對下圖應該比較熟悉:
    TCP三次握手圖解

對上圖中的代號及下文中的代號做說明:

  • Seq即下文中的Sequence number ,序列號是指傳送資料的位置。每傳送一次資料,就累加一次該資料位元組數的大小。一般用Wireshark捕獲我們平時的請求的Seq是一個隨機數。
  • AckAcknowledgement number,是指下一次應該受到的資料的序列號。
  • SYN為Flags部分的Syn,Syn為1表示希望建立連線。
  • ACK為Flags部分的ACK,Ack為1表示確認應答的欄位變為有效。

TCP第一次握手,客戶端向服務端傳送報文,關鍵資訊為Syn=1,Seq=0。如下圖所示,sequence number= x = 0,Syn=1。

TCP第一次握手

TCP第二次握手,服務端向客戶端傳送報文,關鍵資訊為Ack=x+1=1,Syn=1,Seq=y=0。如下圖所示,sequence number=y=0,Ack=x+1=1 , Syn = 1。

TCP第二次握手

TCP第三次握手,客戶端向服務端傳送報文,Seq=x+1=1,Ack=y+1=1,ACK=1。如下圖所示,Seq=x+1=1,Ack=y+1=1,ACK=1。

TCP第三次握手

我們可以發現在三次握手之後,還有一次TCP Window Update。

TCP Window Update

TCP Window Update 是TCP通訊中的一個狀態,它可以發生的原因有很多,但最終歸結於傳送者傳輸資料的速度比接收者讀取的資料還快,這使得接受端的在緩衝區必須釋放一部分空間來裝傳送過來的資料,然後向傳送者傳送Windows Update,告訴給傳送者應該以多大的速度傳送資料,從而使得資料傳輸與接受恢復正常。參考:tcp三次握手

從上圖TCP Window Update,根據Source Port:20000及Destination Port:59006可知,當前傳送者是客戶端,解釋下上一段文字的意思是,客戶端傳送的資料太快,服務端讀書資料慢,服務端向客戶端傳送了一個TCP Window Update的報文。

上述內容就是TCP建立連線的過程,下邊筆者給大家介紹下傳輸資料部分的內容:

TCP的資料傳輸過程:

檢視資料傳輸過程和之前建立連線部分,用的是下圖程式碼進行的分析:

初探TCP
程式碼內容和之前建立連線的程式碼一樣,只是新增了傳送資料斷開連線的幾行程式碼。可見這次客戶端分配的埠號為53262。 在分析資料傳輸過程之前,筆者先對下邊會用到的名詞及工具做個簡單說明:

位元組byte位元bit,1個位元組(byte)=8個位元(bit)。

ASCII碼:是基於拉丁字母的一套電腦編碼系統,主要用於顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統

ASCII碼對照表

舉個例子'A'的ASCII碼為0x41

基本的16進位制、2進位制、10進位制之間的轉換

16進位制0x41對應2進製為 0100 0001對應10進製為4 * 16 + 1 = 65

線上進位制轉換

下邊筆者帶大家看一下資料傳輸部分的分析:

下圖表示客戶端s.send(b'A')以二進位制形式傳輸'A'(其對應的ASCII碼為65)傳輸過程,:

客戶端到服務端

下邊展示一個客戶端s.send(b'AB')並且服務端給予相應的迴應(服務端也給客戶端傳送收到的'')的過程

  • 接收資料的部分Flags中的Acknowledgement 設定為1,表示確認應答的欄位有效
  • 接收資料的部分Flags中的Push 設定為1表示表示接收方應該儘快把資料傳給上層應用協議

源埠53262目的埠20000可以看出,下圖表示客戶端向服務端傳送訊息,傳送的資料為'AB','AB'的ASCII碼為0x4142。

客戶端向服務端傳送訊息
源埠20000目的埠53262可以看出下圖表示服務端向客戶端反饋收到了訊息。Acknowledgement number 為4是因為,服務端接接收了客戶端的2個位元組的資料,在之前的客戶端的Sequence number的基礎上加了2。
服務端到客戶端收到訊息響應
源埠20000目的埠53262可以看出,下圖表示服務端向客戶端傳送訊息,傳送的資料為'AB','AB'的ASCII碼為0x4142。
服務端給客戶端傳送訊息
源埠53262目的埠20000可以看出下圖表示客戶端向服務端反饋收到了訊息。Acknowledgement number 為4是因為,客戶端接收了服務端的2個位元組的資料,在之前的服務端的Sequence number的基礎上加了2。
客戶端收到服務端訊息後的響應

TCP斷開連線四次揮手

TCP斷開連線示意圖如下:

TCP斷開連線示意圖

對應的Python的客戶端程式碼s.shutdown(2),客戶端主動斷開連線的。

  • 斷開連線的Flags中Fin是設定為1的,表示希望斷開連線。
  • 斷開連線的Flags中Ack是設定為1的,表示確認應答欄位有效。

響應的Wireshark抓包分析如下: TCP斷開連線第一次揮手,從源埠53262到目的埠20000,可以看出是客戶端主動斷開連線的。Flags中的Fin是設定為1的,Sequence number為7。

TCP斷開連線第一次揮手

TCP斷開連線第二次揮手,從源埠20000到目的埠53262,可以看出是服務端給予客戶端斷開連線的響應。並且Acknowledge number對之前的客戶端的Sequence number做了加1操作。

TCP斷開連線第二次揮手
TCP斷開連線第三次揮手,從源埠20000到目的埠53262,Flags中的Fin是設定為1的,可以看出是服務端向客戶端傳送斷開連線的。Sequence number為7。
TCP斷開連線第三次揮手
TCP斷開連線第四次揮手,從源埠53262到目的埠20000,可以看出是客戶端給予服務端斷開連線的響應。並且Acknowledge number對之前的服務端的Sequence number做了加1操作。
TCP斷開連線第四次揮手

下邊,筆者貼出了IP和TCP首部及Wireshark捕獲TCP請求過程的的圖。有興趣的讀者可自行做簡單分析。

TCP資料在IP資料包中的封裝及TCP包首部

  • 後來和昆哥一起交流,經過昆哥指正,上圖的TCP首部已經更新過了,較新的TCP首部格式如下:
    TCP首部
    下圖為TCP首部中的控制位部分
    控制位 Control Flag

TCP

TCP

參考內容:


小編微信:可加並拉入《QiShare技術交流群》。

初探TCP

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:
iOS 多執行緒之GCD
iOS 多執行緒之NSOperation
iOS 多執行緒之NSThread
iOS Winding Rules 纏繞規則
iOS 簽名機制
iOS 掃描二維碼/條形碼
奇舞週刊