TCP/IP 協議
是網絡通訊的基石,TCP/IP 協議
不是隻有 TCP
和 IP
協議,它是整個網絡通訊中全部協議的簡稱。php
維基百科:TCP/IP協議簇)html
維基百科:OSI模型java
# TCP/IP 參考模型維基百科 https://zh.wikipedia.org/wiki/TCP/IP%E5%8D%8F%E8%AE%AE%E6%97%8F # OIS 參考模型維基百科 https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B
<img src="http://oss.mflyyou.cn/blog/20200801104517.png?author=zhangpanqin" alt="image-20200801104517510" style="zoom:50%;" />linux
圖片來自 《圖解 TCP/IP 與 OSI 參考模型》 中 TCP/IP 協議分層模型
OSI 參考模型
(七層)是個理論模型,實際咱們用的是 TCP/IP
(四層)模型。不過咱們能夠經過 OSI 參考模型
來學習 TCP/IP
模型。面試
應用層:應用程序通訊細節的協議,好比經常使用的 HTTP
。redis
傳輸層:主要是負責兩個節點之間數據傳輸,通訊標識是 port
端口號。算法
網絡層:地址管理和路由選擇,在兩點之間找到一條最佳的通訊路線,通訊標識是 IP
後端
數據鏈路層:負責物理層面連接的通訊(同一個網段內)。也就是局域網中經過交換機連接的節點。通訊標識是 Mac 地址
,網卡出廠自帶的標識。centos
物理層:將鏈路層的數據幀(字節流)轉換爲電壓或光信號傳播。數組
網絡通訊能夠作什麼呢?
redisson
(一個操做 redis 的 java 庫),就是使用的 netty
來作網絡通訊鏈接 redis 服務的。
微服務中的服務發現和通訊,就須要你熟悉網絡通訊。
你要是在通訊行業,那就不是瞭解了,你連協議的規範都得很清楚,否則路由器你都整不出來,還說什麼 5G
。
做爲一個 Java 後端開發,主要是開發偏應用層面的程序,離底層相對比較遠,熟練掌握便可,若是之後作通訊行業的時候,你也必定會進一步學習的相關細節的。
TCP/IP
你不瞭解,也不會有多大問題,CRUD
仍是沒有問題的。可是你瞭解了以後,平常開發定位和解決問題方面有很大助力,總之學習 TCP/IP
是一個重要不緊急的事情,根據本身目標和層次安排。
幀
數據格式交換機上有多個端口(不是 port)供計算機鏈接,交換機會維護端口與鏈接這個端口的 PC 的 Mac 地址映射表。當交換機接受到數據的時候,會根據目的 Mac 地址,發送到對應的端口上,而後通過網線發送到目的 PC。
交換機連接多個電腦組成一個局域網,交換機連接交換機又能夠組成一個更大的局域網。
好比 A、B 交換機各有 100 個端口,A 連接了 99 個PC,而後 B 交換機連接99 個,再將其中的一個端口 A/B 之間相互鏈接組成一個更大的局域網。
路由器工做在網絡層,主要用於將一個網段數據包轉發到另外一個網段內。路由器上也會有個幾個 LAN
口 (Local Area Network,局域網),用於創建局域網。還會有一個 WAN
(Wide Area Network,廣域網),鏈接運營商的網絡。
路由器也具備交換機的功能,只是 LAN 口 比較少,能夠接入的電腦比較少。
當 PC
或者 手機
鏈接無線路由器時也會給 PC
分配一個局域網 IP
,子網掩碼,網關等。
我住的地方的網絡拓撲圖以下:
當手機與電腦通訊的時候,實際經過 LAN
口走局域網通訊。
當手機訪問 維基百科
時,實際是經過路由器跳入到光貓網段,再經過光貓跳入到小區運營商的網絡,… 到維基百科的服務器上。
只要須要有 IP 地址的設備(光貓,路由器,PC,手機)都須要有網卡,網卡出廠自帶有 Mac 地址。IP 和 Mac 地址的做用後文中會介紹。
<font color=red>這部份內容是我本身的理解,我沒有在網上找到資料佐證,請謹慎對待</font>
其實交換機和路由器硬件差異不大,只是硬件上的軟件決定了它能作什麼。
2 層交換機上的軟件(只有數據鏈路層)可能只作解析幀,拿到 mac 地址,而後查找當前交換機的端口對應的 mac 地址,而後從對應的端口傳遞過去。
路由器(有網絡層和數據鏈路層),當拿到數據包的時候,發現目的 mac 地址不是本身,就會將數據包經過 LAN 口發送出去。
當發送的數據包的 目的 MAC 地址
是當前路由器上 MAC地址
,路由器就會對其解包,拿到數據包 目的 IP
,而後根據 目的 IP
匹配下一跳 mac 地址,封包爲新的幀數據發送出去。
從發送端發送數據的時候,數據通過每層的封包,經物理層傳送到接收端。接收端收到數據包,一層一層進行拆包,而後將數據數據發送給我接收端的應用層的應用程序。
一般咱們說的第一層就是 物理層
,第二層是 鏈路層
…...
源 MAC 地址
就是發送端的 MAC 地址
,目標 MAC 地址不是最終的 MAC 地址,是下一跳節點的 MAC 地址。
類型
指的是這個以太網幀中的 數據
是何種類型的數據,好比 IPV4,IPV6。而後調用對應的接口進行處理。
數據鏈路層傳輸的幀是有大小限制的(64-1518 字節),能傳輸的數據的最大值就是 最大傳輸單元
,簡稱 MTU
,Maximum Transmission Unit
。這個值在以太網中一般是 1500。
# 查看網卡對應的 MTU ifconfig -a netstat -i
網絡層主要以 IP
協議爲主,也有 ICMP
,ARP(在 TCP\IP 模型
中,arp
屬於網絡層。在 osi 七層模型
,arp
數據鏈路層。)。
IP
是網絡層通訊的標識。可是 IP
不容易記憶,因此出現了 域名
。
訪問 DNS
能夠將域名解析爲 IP
。
能夠在本地配置 host
,定義域名和 IP 對應關係,這樣就不用解析了。
也能夠在電腦配置 DNS 解析時訪問的 ip,這樣域名解析時就會訪問這個服務。
<img src="http://oss.mflyyou.cn/blog/20200801182357.png?author=zhangpanqin" alt="image-20200801182357581" style="zoom:50%;" />
# 解析域名的 ip dig www.mflyyou.cn
IP 地址
又能夠分爲 IPV4
和 IPV6
,目前使用比較廣的是 IPV4
,因此只介紹 IPV4
。
IP 地址
由 32 (2 進制)位組成,32 位被 .
分爲了四組。每組 8 位,十進制表示就是 xxx.xxx.xxx.xxx(xxx 取值在 0-255)。
IP 地址
由 網絡地址
(網段) 和 主機號
。
同一個網段的電腦用 2 層交互機相連,而後就能夠局域網通訊了。
同一個網段內,主機號不能重複,重複主機號的電腦不能上網。
爲了便於區分出 IP 在那個網段,引入了子網掩碼
(netmask)。IP 地址與子網掩碼按位與計算能夠得出網段,32 位 中取出網段所在的位,剩餘就是主機號能取得值。
IP 中主機號全爲 0 就是網段,全爲 1 就是廣播地址。這兩個是不能被分配給電腦的。
IP:192.168.202.116
子網掩碼:255.255.252.0
網段爲:192.168.200.0
廣播地址爲:192.168.203.255
IP:192.168.201.56
子網掩碼:255.255.252.0
網段爲:192.168.200.0
廣播地址爲:192.168.203.255
網絡層是不可靠傳輸,發送失敗的數據包,網絡層是不會再發一次數據包,可是會有 ICMP
包回覆告訴你發包究竟是什麼問題。傳輸層
能夠根據 ICMP
來判斷是否須要重發包。
ARP
用於 IP 的 對應的MAC 地址。
目的 IP 在路由表中查詢下一跳的 IP,在查詢這個 IP 對應的 mac 地址
查詢的這個 IP
是當前網段內的 ip,它會經過廣播地址發送給當前網段內全部主機,收到這個協議的主機會判斷是不是當前主機,是的話就會恢復當前 ip 對應的 MAC 地址。
當我在瀏覽器輸入 wwww.mflyyou.cn
的時候:
一、先解析域名(DNS) www.mflyyou.cn
爲 IP
(目的 IP: 47.104.168.20)
二、將目的 IP 與本地路由表中的子網掩碼進行按位與,計算出網段與 Destination 匹配,看哪一個匹配度更高,走哪一個條目。都沒有匹配到走默認條目(0.0.0.0)
# 查看路由表 route -n
Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 192.168.31.1 0.0.0.0 UG 100 0 0 eth0
三、而後用 arp 查詢(有緩存可不查,走緩存)192.168.31.1 對應的 mac 地址
四、數據鏈路層封裝以太網幀數據包中的目的 MAC 地址址就是 192.168.31.1
對應的 mac 地址,而後將數據幀發送到下一個節點(這也就常說的下一跳,數據包發送只是找到當前接節點的下一個節點)
五、到下一個路由器節點,路由器解包,看是發給本身的數據包(根據幀中的目的 MAC 地址與本身的 MAC 地址比較),不是就丟棄了;是的話就會解包拿到 目的 IP
(47.104.168.20),而後在當前路由器上根據路由表查詢下一跳,發送給下一個節點;。。。。 直到目的服務器,或者發送的包 TTL
爲 0
六、發到目的服務器的網卡上,網卡將數據複製到內核緩衝區,應用程序從緩衝區中讀取數據
<font color=red>IPv4 數據結構</font>
<img src="http://oss.mflyyou.cn/blog/20200802000153.png?author=zhangpanqin" alt="image-20200802000153692" style="zoom:50%;" />
圖來自《圖解 TCP/IP》
Data
(實際發送的數據)是什麼類型的數據,1 標識 ICMP, 4 標識 IP, 6標識 TCP, 17 標識 UDP。根據這個掛載協議程序就知道調用哪些接口來進行後續的處理了。數據鏈路層中 以太網數據幀
的 MTU
是 1500 字節,限定了 IP 數據包最大爲 1500 字節。而後去掉 IP 包首部 20 字節,通常 IP 數據包發送的數據爲 1480 字節。
當咱們發送一個 3058 字節的 IP 數據包時,這顯然大於了數據鏈路層的 MTU (1500 字節)。因此網絡層會對大於鏈路層MTU 的數據包進行分片。拆分一個一個的1500 的數據包發送接收端,接收端接收到這三個包,在匯聚成一個完成的,在調用傳輸層接口。
# 會發送 3050 字節數據與 8 字節的 ICMP 首部,這個命令會總共發送 ip 數據大小 3058 字節。 ping -s 3050 www.mflyyou.cn
<img src="http://oss.mflyyou.cn/blog/20200801230141.png?author=zhangpanqin" alt="image-20200801230141070" style="zoom:50%;" />
<img src="http://oss.mflyyou.cn/blog/20200801230528.png?author=zhangpanqin" alt="image-20200801230528418" style="zoom:50%;" />
<img src="http://oss.mflyyou.cn/blog/20200801230423.png?author=zhangpanqin" alt="image-20200801230423653" style="zoom:50%;" />
經過 wireshark
抓包能夠看到,IP 數據包的首部長度佔了 20 字節,實際每次發送數據爲 1480 字節,最後一次發送了 98 字節。
從 Fragment 和 Identification 能夠看到這三個包屬於同一個 IP 數據包,而且從 Fragment offset
能將這三個包合成一個完成的網絡層數據包。
TCP
是面向連接的,可靠的,全雙工協議。
面向鏈接就是發送以前,須要創建一個連接通道,數據都是在這個連接中發送。
網絡層
是不可靠協議,數據發送失敗是不會重發的。
TCP
協議中發送端會記錄發送的那些數據包被客戶端收到了。接收端接受數據以後,會回覆一個 ACK
包(由數據格式中的控制位決定),確認應答號告訴發送端哪些數據包接收到了。
發送端
發送了數據包以後,這個包會有一個重發倒計時,在這個倒計時內沒有收到接收端
回覆 ACK
包,就會再重發一個數據包。若是是 HTTP 請求
,就至關於一樣的數據請求了兩次。
咱們知道支付接口都要求冪等性,有一部分緣由是由於這個超時重發。發送端發送了請求,接收端處理好業務以後回覆的 ACK
包超時,發送端超時重發這個請求。若是不保證接口的冪等性,那麼扣錢就會扣兩次。
咱們要作的就是保證這個重發 n+1 次再也不扣用戶的錢,通常會用一個 token 來判斷是否是重複請求,重複就不走扣款處理了,直接返回已經支付,保證接口的冪等性。或者用一個帳單流水來保證冪等性。
鏈接既然須要創建,那麼也會有鏈接斷開。斷開鏈接需雙方協商好以後斷開鏈接,不能單方面關閉而無論對方。由於創建鏈接以後佔用的計算機資源須要釋放掉。你單方面強制斷開鏈接釋放了資源,可是對方不知道須要斷開鏈接,分配的計算機資源一直佔用那就是不可靠協議了。因此 TCP
有四次揮手斷開鏈接。
全雙工就是鏈接兩邊均可以主動發送接受數據,而不是輪訓訪問有沒有數據到達。
首先咱們要先了解 TCP
數據格式,才能更容易知道 TCP 的工做原理。
<img src="http://oss.mflyyou.cn/blog/20200802000246.png?author=zhangpanqin" alt="image-20200802000246545" style="zoom:50%;" />
佔用 2 個字節。標識 發送端
程序的端口號,當接收端須要回覆消息的時候,須要帶上這個端口號。
佔用 2 個字節。標識 接收端
程序的端口號,能夠傳遞給監聽在這個端口的程序
佔用 6 位,不滿一個字節。標識當前 TCP
包是什麼包,在通訊過程當中有一些特殊做用。
SYN
表示但願創建三次握手連接,並初始化序列號。
ACK
對收到數據包的應答確認。接收端接受數據以後,會回覆 ACK
包,發送端從其上 確認應答號
知道接收端哪些數據已經接受了。
FIN
表示沒有數據發送了,但願斷開鏈接
PASH
接收端接收到這個數據包須要馬上傳遞給應用層,不能等待接收更多的數據包
RET
連接出現異常,須要強制斷開鏈接
URG
表示包中有須要緊急處理的數據
佔用 4 個字節。TCP
三次握手的時候,發送端和接收端各自初始化(隨機的)本身的 `序列號。
咱們能夠這樣理解,發送端發送的數據就是一個字節數組,這個數組中每一個字節都有一個 序列號
。
發送端和接收端都有本身的序列號,而且不相同,在三次握手的時候本身初始化,而後告知對方。
佔用 4 個字節。確認應答號
也是指的序列號,指的是指望發送端下次發送的序列號,這個序列號(確認應答號)以前的數據已經接受處理了。
下圖是我抓包創建三次連接,而後我發送三次 1\n
數據。
三次握手,發送端經過發送 SYN
包,發送本身的初始化序列號(893189542),而後發送的每一個字節都會有一個序列號。
接收端發送 ACK
包中的 確認應答號
,指明這個序列號以前的數據我已經接受了。
<img src="http://oss.mflyyou.cn/blog/20200802205000.png?author=zhangpanqin" alt="image-20200802205000890" style="zoom:50%;" />
窗口大小適用於流控的。發送端不能一直髮送消息,須要根據個人接受能力來調整發包的速率。
內核會爲每一個 TCP/IP
分配讀寫緩衝區,網卡會從這些讀寫緩衝區中把數據取走,而後發送。數據大體能夠分爲這幾類。
TCP/IP
是可靠鏈接,因此它須要記錄哪些數據發送已被對方接受了(由確認應答號能夠知道),接受的數據會被淘汰掉,節省內存空間。
窗口大小做用:接收端會經過 ACK
告訴 發送端
調整窗口大小。
當窗口中的數據全都是 已發送未確認數據
時,發送端不能再發送新的數據,必須等待窗口空出位置來。
當有一個數據包被確認了,發送端就能夠發送新的數據包。已發送未確認數據
會在超時的時候從新發包。
佔用 2 個字節。校驗和
用於校驗數據包是否損壞。每一個數據包都一個 校驗和
,接收端
接收到數據以後,使用相同的算法對數據計算出一個值,而後和 校驗和
比較,不同說明數據在傳輸過程當中損壞了,接收端
會丟棄這個包,等待 發送端
從新發這個包。
鏈路層能發送的最大以太網幀爲 1500 字節,MTU 爲 1500。
IP 數據包能發送的最大數據 = MTU - IP 首部大小(通常 20 字節),IP 數據包超過這個 1500 字節會分片
TCP 傳輸數據以段 (Segment) 爲單位。
TCP 爲了不分片,會主動將數據分片以後交給網絡層。 TCP 能傳輸的最大分段(只是數據不包括首部)稱之爲 Max Segment Size,簡稱爲 MSS。
MSS = MTU - IP 首部大小 - TCP 首部大小
在以太網中 TCP 的 MSS = 1500(MTU) - 20(通常 IP 首部大小) - 20(通常 TCP 首部大小)= 1460,這個值須要根據首部計算
MSS 值在三次握手時,會經過 MTU 計算的。
<img src="http://oss.mflyyou.cn/blog/20200802212532.png?author=zhangpanqin" alt="image-20200802212532628" style="zoom: 33%;" />
圖片來自 碼出高效:Java 開發手冊
爲何是三次握手創建鏈接呢?不少面試官也會問。這實際上是可靠鏈接的最少握手次數。
<img src="http://oss.mflyyou.cn/blog/20200802212808.png?author=zhangpanqin" alt="image-20200802212808724" style="zoom:50%;" />
圖片來自 碼出高效:Java 開發手冊
這裏還有個 全鏈接隊列和半連接隊列 的知識點
<img src="http://oss.mflyyou.cn/blog/20200802213247.png?author=zhangpanqin" alt="image-20200802213247725" style="zoom: 33%;" />
圖片來自 碼出高效:Java 開發手冊
CLOSE_WAIT
是收到對方 FIN
包以後,回覆 ACK
以後進入的狀態。以後不會接受數據了,進行已收數據的業務處理以後,在發送一個 ACK+FIN
,進入 LASK_ACK
,而後等待對方發送 ACK
,超時沒有等到,會重試發送(內核能夠配置重試發送次數)。當你發現服務端有大量的 CLOSE_WAIT
連接,服務端的代碼有問題,須要排查。
TIME_WAIT
的連接多的話,服務端能夠優化,否則這個連接會佔用很長時間,在高併發的時候,會致使沒有資源釋放的慢。
MSL 爲 Maximum Segment Lifetime,在 centos 中默認值爲 60s
# sysctl -a | grep tcp_fin_timeout # 推薦小於 30,也不能過小,15-30 net.ipv4.tcp_fin_timeout = 60
說明 A 機器連接會在 120 s 以後才能釋放。這個是爲了保證 B 機器 能接收到最後一個 ACK
,當處於 LAST_ACK
的超時沒有收到A 發來的 ACK
的話,會重試發送一個 FIN+ACK
。這個 2MSL 也是爲了最大限度保證 B 機器正常關閉。
三次握手創建鏈接
和 四次揮手斷開鏈接
須要結合抓包工具本身分析一下,理解會更深入。
Wireshark
抓包分析是很厲害的,mac os
和 linux
都有命令行程序 tshark
,能夠在服務器用 tshark
抓包,拿到本地來分析。
抓包的時候必定要指定抓什麼包,什麼包都抓的話,一會你的電腦內存就飆升好多(別問我爲啥知道,問就是 30g 內存都讓它吃了)。
Wireshark
有個 抓包過濾器
和 顯示過濾器
。抓包的時候指定抓什麼包這是 抓包過濾器的做用
,抓包以後顯示顯示那些內容那是 顯示過濾器的做用
# -i 指定那個網卡 # -f 指定抓包過濾器 # -Y 顯示過濾器 # -w 指定抓包數據到文件,沒有 -w 輸出屏幕 # -V 顯示 TCP/IP 每層包的詳細信息,建議將抓包的文件在圖形化界面中查看,不指定 -V tshark -i en0 -f "tcp" -Y "http" # 抓取訪問 www.mflyyou.cn 的包 tshark -i en0 -w a.pcap -f "host www.mflyyou.cn" # 指定抓那個協議 tcp,ip,icmp,arp,udp tshark -i en0 -f "tcp" # host 指定域名或者 ip # port 指定端口 # 訪問 www.mflyyou.cn 的包,或者 icmp. ping www.baidu.com 也會被抓到 tshark -i en0 -f "host www.mflyyou.cn || icmp" tshark -i en0 -f "port 80" # 條件之間支持邏輯運算符 || && ! # 抓取 ssh 連接的包 tshark -i en0 -f "host www.mflyyou.cn && port 22"
《圖解 TCP/IP》
本文由 張攀欽的博客 http://www.mflyyou.cn/ 創做。 可自由轉載、引用,但需署名做者且註明文章出處。如轉載至微信公衆號,請在文末添加做者公衆號二維碼。微信公衆號名稱:Mflyyou