---
title: '傳輸層 - TCP / UDP'
disqus: kyleAlien
---
傳輸層 - TCP / UDP
===
## OverView of Content
如有引用參考請詳註出處,感謝 :smile:
傳輸層的協定能讓網路層(IP)的包、應用層的需求連接,其中最常見的就是 TCP、UDP 協議
[TOC]
## 傳輸層 概述
### TCP 概述
* TCP 是傳輸層的一項協定,TCP 之所以流行是因為它對應用沒有太多要求,只須知道開啟、讀取、寫入、關閉... 等等行為即可(就跟處理檔案差不多)
* TCP 的實現大多都包裹在系統核心之中,它幫我們處理了以下事項
1. 傳送時將數據切分打包
2. 接收時將數據拆分
3. 檢查錯誤、盡可能的修復
> 當然如果我們停留在應用層級是不需要知道這麽多資訊,大致如下圖
``` mermaid
graph TD;
客戶端發送-HelloWorld-->客戶端核心-拆數據-He_llo_Wor_ld;
客戶端核心-拆數據-He_llo_Wor_ld-->Internet;
Internet-->遠端核心-解數據-He_llo_Wor_ld;
遠端核心-解數據-He_llo_Wor_ld-->遠端核心組裝&修復;
遠端核心組裝&修復-->遠端應用收到-HelloWorld;
```
### UDP 概述
* UDP 單次傳輸就可以構成完整的傳輸(不用糾正封包、不關心亂序),但 **UDP 仍有連接埠(Port)的概念**
:::warning
* 雖然 UDP 有連接埠的概念,不過它不用使用連接埠來進行連接!該連接埠只是用來發送資料,而遠端是否要回覆都可以
> 所以 UDP 的速度會比 TCP 快,但相對的不穩定
:::
## 連接埠 Port
就算視同種類的服務,只要編號(連接埠 Port)不同,該服務就可被視為令一個連線
> 連接埠 Port 是一個數字
### IP & Port 關係
* 簡單來說 **Mac、IP 是++唯一++的地址,就像大樓門牌號碼**
> 
* Port 是在同地址的下,不同的分配位子
> 
### TCP 連接埠、連接 - netstat
* 使用 TCP 連線時,應用會在本地開啟一個連接埠(Port)與遠端主機的一個連接埠(Port)連接
``` mermaid
graph TD;
本機Port_12345-->遠端Port_443;
```
:::warning
* 這種 Port 的連接是建立在 NetworkManager 連線之上
:::
可以使用 `netstat` 觀察當前主機與哪些遠端的 Port 有連接關係
```shell=
# -n 不做 DNS 解析
# -t 只顯示 tcp
netstat -n -t
```
> 
### 伺服器 & 常見 Port
* 什麼是 Port 號 ? 是拿來幹嘛的 ? 就像是 **同大樓中共用地址 (可能地址都是 123),但有不同的樓層房號 (房號 1 ~ 50 號) 這就是 Port 號**
:::success
* Port 號有幾個呢 ?
**一個 Port 規定為 16 位元,也就是 2^16^ 個不同端口**
:::
* 一台伺服器通常會處理不只一種服務,有可能有網頁、郵件、檔案傳輸... 等等,如果要從另外一台裝置透過外部訪問(網路 IP),就需要指定 Port 號
> 不同 Port 代表不同服務
其中有包含 **特殊端口號**
| 端口號 | 功能 | 補充 |
| -------- | -------- | -------- |
| 0 ~ 1023(2^10^) | 系統端口號(標準、知名埠) | 不可隨意使用,`Http:80`、`FTP:21`、`TELNET:23` |
| 1024 ~ 49151 | 登記端口號 | 讓第三方有登記的應用使用,透過 IANA 登記 |
| 49152 ~ 65535 | 暫時端口號 | 可以自由使用,通常由系統隨機挑選使用(以保證不會發生衝突) |
* 透過通訊埠編號(Port)識別時,我們有幾個常見的公認通訊 Port(Well-Known),**範圍為 ++0 ~ 1023++**,**在這個範圍內的 Port 請不要隨意佔用**
| 常見 Port 號 | 用途 |
| -------- | -------- |
| 20 | FTP 傳送資料 |
| 21 | FTP 控制 |
| 22 | SSH (Secure Shell) |
| 23 | Telnet 傳送 **未加密** 內容的文字傳輸協定 |
| 25 | SMTP 接收、發送郵件 |
| 80 | HTTP 網頁伺服器 |
| 110 | POP3 接收郵件 |
| 123 | NTP NetWork Time Protocol |
| 443 | Https 在 Http 基礎加上 SSH 加密傳輸內容 |
:::info
- 在由瀏覽器上基本是不需要指定 Port 號
```shell=
www.google.com:443
```
:::
另外我們可以查看 `/etc/services` 文件看看有哪些公認的連接埠
```shell=
cat /etc/services
```
> 
### 路由器的 Port
* 在路由器內也有使用通訊埠編號 (Port),內部大部份是內建了
1. NAT (`Network Address Translation`):將全球 IP 位址轉為私人 IP 位址
> 這樣就換變成 1 個 全球 IP 位址對應 多個私人位置
``` mermaid
graph TD;
全球_IP-117.122.1.32-->路由器_NAT;
路由器_NAT-->裝置A_B_C-117.122.1.32;
```
2. NAPT (`Network Address Port Translation`):在 NAT 的機制下加上 Port 號,讓每個私人 IP 都有 Port 號
``` mermaid
graph TD;
全球_IP_117.122.1.32-->路由器_NAPT;
路由器_NAPT-->裝置A_117.122.1.32:80001;
路由器_NAPT-->裝置B_117.122.1.32:80002;
路由器_NAPT-->裝置C_117.122.1.32:80003;
```
### Port 建立 TCP 連接
* 要建立傳輸層連接,應用會先發送一系列特別的封包,先初始化一個本機連接埠(Port)到選端的連接埠
* **客戶端**:通常發起連接端稱之為客戶端
> 客戶端 Port 通常是隨意挑選一個未使用的 Port,這種 Port 又稱為 **動態連接埠**
* **伺服器**:接收(監聽)資料端稱為 伺服器
> 伺服器端的 Port 則是固定公認的
可以使用 `netstat` 查看本地正在監聽的 Port
```shell=
# -l 監聽中
netstat -n -t -l
```
> 下圖中可以看到某些服務正監聽本地(127.0.0.1)的 25, 631, 5939 連接埠
>
> 
:::info
* 可以使用 `lsof` 搭配 `grep` 命令查看是哪個應用正在監聽該連接埠
:::
## TCP & UDP
* TCP / UDP 為++全雙工++;並且該協議是 **==傳輸層== 控制協定,TCP 又稱為++可靠協議++,UDP 又稱為++不可靠協議++**,以下會詳細介紹 TCP 的握手協定
:::info
* 那是否每個通訊方式都需要經過 OSI 協定的傳輸層呢?
不用,並非每個傳輸協定都要經過傳輸層;像是 **Ping 傳輸命令就不需要經過傳輸層**(它基於網路層協定運作)
:::
> 網路層: ICMP 控制報文協議、IGMP internet 管理協議
>
> 鏈路層: ARP 地址解析、RARP 地址加密協定
> 
:::info
Android 最底層的是使用工具 **`tcpdump`** (指令式)、上層使用 `fiddle`、`charls`,並使用 `waveshark` 分析
:::
### TCP 數據包結構
* 每一次都會傳輸都會發送這種格式的數據,**在三次握手 \ 四次揮手時會操作到一系列的 Flag (紅色框)**
> 
### 三次握手
* 小寫代表序列號,大寫代表數據包中的 Flag 狀態,**注意流程**
1. **第一次 (Client to Server) :** `SYN = 1`、**`seq = J` ++系統隨機生成、不可固定++ (因為數據如果斷線後重連,同序列號會導致無法確認是否是同一資料)**
2. **第二次 (Server to Client) :** `SYN = 1`、`ACK = 1`、**`ack = J + 1` (才能確認是否有收到),seq = K 改為 established 建立連接狀態**
3. **第三次 (Client to Server) :** `ACK = 1`、`ack = K + 1`
* 交換完初始序號後,就可以開始傳送數據了,**如果連上後不發送數據會自動斷開 (超時機制)**
> 
:::warning
* **為何是三次握手、不是四次、五次 ?**
> **主要是為了 TCP 的考靠性,該可靠性是在確認序列號 (比對序列號)**,**==主要是為了++交換 TCP 的初始序列號 ISN++==**,這樣才能確認是否是相同連線,3 次以上握手是多餘的
:::
### TCP 握手漏洞 - STN Flood
* **SYN 攻擊,大量發送偽造園地址的攻擊報文,發送到服務端,造成服務端上的==半開連結隊列溢出==,阻止其他用戶訪問**
> 原理 :
>
> Client 偽造 IP 地指向 Server 端發出請求 (一次握手),而服務端自然響應 (二次握手) 但無法發送到指定 IP,所以**服務端會一直等待第三次握手,而處於半開連結狀態 (等待第三次握手)**,這會導致資源耗損
:::success
**解決方案: 無效連接監控釋放、防火牆**
:::
### 四次揮手
* 斷開一個 TCP 連接時,需要客戶端共發起 4 次確認訊息,C/S 都可以發出揮手,以下以客戶端主動斷開
1. **Client :** `FIN = 1` **(發起斷線),Client 狀態轉為 FIN_WAIT_1**
2. **Server :** `ACK = 1` **(同意斷線),Client 狀態轉為 FIN_WAIT_2,轉為半連接狀態**
3. **Server :** `FIN = 1` **(確定斷線請求)**
4. **Client :** `ACK = 1` **(發送確定斷線請求),Client 狀態轉為 TIME_WAIT,並==等待 2MSL 的時間,超過後就默認真正斷開==**
* **MSL : Maxnum Segment Lifetime**,2MSL 是數據包來回時間
> 
:::warning
* **為何是四次揮手 ? 三次可以嗎 ?**
> 因為是 **++全雙工++ 的原因**,導致雙方都要確認斷線需要四次,但是**要三次也是可以 (Server、Client 端的 FIN 同時發送)**
* 為何需要 `TIME_WAIT` ?
1. 保證 Server 端真的接收到 Client 端斷線訊息,這樣 Server 端才可以正確的關閉連線
2. 保證 Client 端可以收到 Server 端回覆的訊息,這樣 Client 端才可以正確地關閉連線
3. 意外連線關閉
* 如果超過 `TIME_WAIT`,雙方也會進行 Port 關閉
:::
## TCP 通訊原理
### Socket
* TCP 用主機的 `IP 地址` + `Port 號` 作為 TCP 連接的端點,這種端點稱為 Socket
* TCP 的 Socket 的**內核中都有一個發送緩衝區和一個接收緩衝區**
### 可靠性
* 除了透過之前說過的三次握手++交換序列號++,還有一個 **==確認號==,確認每次數據的傳輸,如下圖**
> 
* 當數據接收失敗 or 遺失,**過一段++特定時間間格++就會在傳輸一次**
> 
* 因為 TCP / IP是全雙工,不會有一傳一收的問題,也就**沒有先發先到的問題**
:::success
* 如何知道數據發送完畢 ?
> 收到確認性信號,就可以保證傳完,而傳到哪裡為止是由上層應用層決定
:::
### 窗口機制
* 滑動窗口機制,這個窗口就包含在 TCP 的數據包中,而**確切的窗口大小是由 ==接收端決定==,因為接收端可以調整其數據接收的大小** (能力強就可調高)
1. 保證數據不會因為緩衝不足而丟失,當傳超出緩存時就會要求重新傳
2. 可調整傳輸速度
> 
## 傳輸層 - 工具
### 連線工具 `netcat`、`telnet`
* 我們可以透過 `telnet`、`netcat` 指令來連接傳輸層
* 使用 `telnet` 連接 google
```shell=
telnet www.google.com 80
```
> 
* 使用 `netcat` 連接 google
```shell=
netcat www.google.com 80
```
> 
### `nmap` - 查看主機 Port
* `nmap` 指令可以查看目標主機有開放哪些 Port 可以給我們使用
:::success
* 最好使用兩個角度(本機、另一個網域的裝置)來查看 `nmap` 指令,因為該指令獲取到的資訊可能不是第一手(**有可能已經被防火牆過濾完**)
:::
```shell=
# 查看 google 主機的 ip 位置
host www.google.com
# 查看 google 網域開放了哪些可訪問的 port
nmap 142.251.43.4
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `網路基礎`