TCP/IP協議知識科普

簡介

本文主要介紹了工做中經常使用的TCP/IP對應協議棧相關基礎知識,科普文。html

本博客全部文章:http://www.cnblogs.com/xuanku/p/index.htmllinux

TCP/IP網絡協議棧

TCP/IP網絡協議棧分爲四層, 從下至上依次是:編程

  1. 鏈路層緩存

    其實在鏈路層下面還有物理層, 指的是電信號的傳輸方式, 好比常見的雙絞線網線, 光纖, 以及早期的同軸電纜等, 物理層的設計決定了電信號傳輸的帶寬, 速率, 傳輸距離, 抗干擾性等等。服務器

    在鏈路層自己, 主要負責將數據跟物理層交互, 常見工做包括網卡設備的驅動, 幀同步(檢測什麼信號算是一個新幀), 衝突檢測(若是有衝突就自動重發), 數據差錯校驗等工做。cookie

    鏈路層常見的有以太網令牌環網的標準。網絡

  2. 網絡層併發

    網絡層的IP協議是構成Internet的基礎。該層次負責將數據發送到對應的目標地址, 網絡中有大量的路由器來負責作這個事情, 路由器每每會拆掉鏈路層和網絡層對應的數據頭部並從新封裝。IP層不負責數據傳輸的可靠性, 傳輸的過程當中數據可能會丟失, 須要由上層協議來保證這個事情。tcp

  3. 傳輸層高併發

    網絡層負責的是點到點的協議, 即只到某臺主機, 傳輸層要負責端到端的協議, 即要到達某個進程。

    典型的協議有TCP/UDP兩種協議, 其中TCP協議是一種面向鏈接的, 穩定可靠的協議, 會負責作數據的檢測, 分拆和從新按照順序組裝, 自動重發等。而UDP就只負責將數據送到對應進程, 幾乎沒有任何邏輯, 也就是說須要應用層本身來保證數據傳輸的可靠性。

  4. 應用層

    即咱們常見的HTTP, FTP協議等。

這四層協議對應的數據包封裝以下圖:

四層數據包

四層協議對應的通訊過程以下圖:

四層通訊過程

鏈路層 以太網數據幀

以太網數據幀格式以下:

以太網數據幀

說明以下:

  1. 目的地址和源地址是指網卡的硬件地址(即MAC地址), 長度是48位, 出廠的時候固化的。
  2. 類型字段即上層協議類型, 目前有三種值: IP, ARP, RARP。
  3. 數據對應了上層協議傳輸的數據, 以太網規定數據大小是46~1500字節, 最大值1500即以太網的最大傳輸單元(MTU), 不一樣網絡類型有不一樣MTU, 若是須要跨不一樣類型鏈路傳輸的話, 就須要對數據進行從新分片。
  4. CRC是數據的校驗碼, 確保數據傳輸正確

ARP協議

在網絡通訊過程當中, 源主機的應用程序只知道目的應用程序的IP地址, 並不知道對方主機的硬件地址, 因此在數據發送以前, 須要先找到目標及其的硬件地址, 這就是ARP協議所起的做用了。

每次在創建鏈接以前, 會在本地網絡廣播發送目的IP地址, 全部機器都會受到該請求, 目的機器發現該請求中的IP地址跟本身同樣, 就把本身的硬件地址返回回去, 不然忽略該請求。

通常來講, 每臺機器都維護的有一個ARP緩存表, 存儲了近期的IP地址和硬件地址的映射關係, 能夠用arp -a命令來查看緩存表中內容。

若是目的機器和本機器不在同一個網段以內的話, 會將數據發送給網關來處理, 通常網關就是路由器, 此時網關會進行IP路由, 將ARP請求發送到目的網絡地址, 而後再依次將應答返回給該發起請求的機器。

IP協議

IP協議數據包格式以下:

IP協議數據格式

幾個字段解釋以下:

  1. TOS, 一共有8位, 其中3位用來表示該數據包的優先級, 目前已經不用; 還有4位表示可選的服務類型(最小延遲, 最大吞吐, 最大可靠性, 最低成本), 還有一位老是0;
  2. 標誌位: 用來對每一個IP包的分片關係進行標識, 用於分片和從新組裝數據包;
  3. TTL(Time To Live), 是指一個數據包在網絡上的最多通過多少次轉發, 若是超過該數字, 就丟棄該包
  4. 8位協議, 上層可選協議爲: TCP, UDP, ICMP, IGMP

IP地址的一共分爲以下幾類:

在互聯網剛出來的時候, 大部分組織都申請的B類網絡地址, 致使B類地址很快就用完了, 可是A類又有不少空閒的地址, 而每一個路由器又必須掌握全部網絡的信息, 隨着C類網絡的增多, 路由器中的路由表項數也就愈來愈多了。

針對這種狀況, 後來人們發現, 絕大部份內部網絡的機器都不須要一個獨立的公網IP的, 這些機器經過一個公網IP跟外部鏈接, 在本身的網絡內部爲每臺機器申請一個私有IP, 內部再建設一個路由器, 作內網IP地址的定位便可。

私有IP的出現大大解決了IP浪費的問題, 因此咱們平常中能夠看到不少如192.168.xx這樣的IP, 這些IP都只是局域網內部IP, 不會浪費IP地址。

因而, RFC1918就規定了組建局域網的私有IP地址規範:

  1. 10.*, 前面8爲是網絡號, 共16,777,216個私有IP
  2. 172.16.*到172.31.*, 共1,048,576個私有IP
  3. 192.168.*, 共65536個私有IP

這些私有IP地址雖然沒有公網IP, 可是仍然能夠經過NAT等技術來跟公網進行鏈接交互。

除了私有IP以外, 還有幾種特殊的IP地址:

  1. 127.*的IP地址用於本機環回測試, 這類地址的交互數據不會過網卡, 直接在內核過一遍協議就完成交互了
  2. 255.255.255.255, 這是個特殊IP, 表明在本地路由廣播
  3. 主機號部分全是0的地址表明一個網絡, 而不能表明某個主機(好比不能用192.168.0.0做爲某臺機器的IP)
  4. 主機號部分全是1的地址表明在該網絡內部廣播

TCP協議

數據包格式

TCP協議數據包以下:

TCP協議數據包

部分字段解釋以下:

  1. 源端口號和目的端口號: 用來標註數據交互雙方進程
  2. 32位序號和32位確認序號: TCP是一個可靠的交互協議, 這兩個序號用作傳輸過程當中數據的標記, 保證數據的傳輸順序以及重發
  3. URG/ACK/PSH/RST/SYN/FIN: 用來標記該請求包位於TCP鏈接中的什麼階段, 這6個字段下面會詳細解釋

交互過程

TCP數據交互

上圖中每次鏈接線上的數字標記了這次數據包中的關鍵信息, 好比

  1. SYN,1000(0),<mss 1460>表明: 請求包包含SYN標記, 32位序號爲1000, 不包含數據, 帶有一個mss的選項, 其值爲1460
  2. SYN,8000(0),ACK,1001,<mss 1024>表明: 請求包包含SYN和ACK標記, 32位序號爲8000, 不會包含數據, 32位確認序號爲1001, 一樣帶有mss選項

那麼接下來咱們看TCP協議的交互過程:

  1. 創建鏈接

    1. 客戶端發送包1, SYN表明請求創建鏈接, 第一個包序號爲1000, 該序號的大小由操做系統內核維護, 每次發送都會自增, 自增數值就是發送的字節數, 其中mss選項表明最大段尺寸, 這是爲了不沒必要要的底層協議的拆包解包;
    2. 服務器返回包2, 包含的ACK 1001, 表明小於1001序號的包我都收到了, 下次請求發送大於等於1001包; 在該包中同時包含SYN 8000(0), 這段跟客戶端交互的時候同樣, 只是服務器端這頭的序號爲8000;
    3. 客戶端返回包3, 裏面只包含ACK 8001的包, 表明收到服務器的創建鏈接的包了

    至此, 鏈接創建完畢, 能夠發送數據了, 該過程包含了客戶端和服務器各一次請求和應答, 服務器的請求和應答放到一個包中作了, 一共包含3次包發送, 因此該過程又被稱爲三次握手。

  2. 交換數據

    1. 客戶端發送包4, 包含ACK 8001, 以及序號從1001~1020的20個字節的數據
    2. 服務器返回包5, 包含ACK 1021(由於包含20個字節), 以及序號從8001~8010的10個字節數據
    3. 客戶端返回包6, 由於數據已經交互完畢, 因此只包含一個ACK 8011

    這一段主要是要理解TCP交互的序號管理邏輯, 由於是全雙工協議, 即服務器和客戶端能夠同時像對方發送數據, 因此須要客戶端和服務器各維護一個序列號。若是是半雙工協議的話, 就只須要一方維護一個序號便可。

  3. 關閉鏈接

    1. 客戶端發送包7, 包含FIN標記, 1021
    2. 服務器返回包8, 只是應答ACK 1022
    3. 服務器再次返回包9, 包含FIN標記, 8011序列
    4. 客戶端返回包10, 包含ACK 8012

    在創建鏈接的時候, 服務器的請求和應答是合併到了一個包當中。可是在關閉鏈接的過程當中, 就必須分開兩個包來, 由於客戶端關閉鏈接以後就不能再發送數據了, 可是服務器還能夠發送數據給客戶端, 直到服務器也發送FIN標記。

滑動窗口

如上講的都是一來一回的交互, 通常狀況下可能會存在一方數據發得特別快, 另外一方數據發得特別慢, 這種時候若是不作控制, 勢必會讓慢的這方數據處理不過來從而致使丟包。

TCP協議中採用了滑動窗口協議來解決該問題, 相似上面的mss, 再增長一個新的選項win, 告訴對方本身的滑動窗口大小, 對方在發送數據的時候每次發送數據就知道對方到底窗口空間還夠不夠, 若是不夠了就不發了, 從而解決了一快一慢這種問題。

鏈接狀態

以下圖:

TCP鏈接狀態圖

其餘狀態都還好, 在工做中常會碰到TIME_WAIT鏈接過多的問題, 這裏把TIME_WAIT狀態單獨拿出來講一下。

TIME_WAIT是主動關閉方在收到被動關閉方發的FIN包以後處於的狀態, 這個包是主動關閉方收到的最後一個包了, 在收到這個包以後還不能直接就把鏈接給關閉了, 還得等待一段時間才能關閉, 等待時間爲2MSL。

爲何要等待一段時間呢? 主要是兩個緣由:

  1. 在收到最後一個包以後主動關閉方還得發一個ACK回去, 這個ACK可能會丟包, 若是丟包, 對方還須要從新發最後一個FIN包, 若是收到從新發過來的FIN包的時候這邊廂連接已經關閉, 則會致使連接異常終止;

  2. 不過第1點也不會形成太大的問題, 畢竟數據已經正常交互了。可是有另一點風險更高, 就是若是不等待2MSL的話, 那麼若是正好一個新連接又創建在相同的端口上, 那麼上次的FIN包可能由於網絡緣由而延時迷途的包這個時候才送達該端口, 致使下一次鏈接出現問題;

因此必定要有一個TIME_WAIT的狀態等待一段時間, 等待的MSL時間RFC上面建議是2分鐘, 可是筆者實際工做中測試每每是30秒。

可是若是你的服務是一個高併發短鏈接服務, TIME_WAIT可能會致使鏈接句柄被大量佔用, 而你又相信服務內部是一個很是穩定的網絡服務, 或者即便有兩個鏈接交互出現故障也能夠接受或者有應用層處理, 不但願有那麼多的TIME_WAIT狀態的鏈接, 通常有兩種方式:

  1. 在創建鏈接的時候使用SO_REUSEADDR選項
  2. /etc/sysctl.conf中加入以下內容:

    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30

    而後執行/sbin/sysctl -p生效參數。

UDP協議

UDP數據格式

UDP協議就簡單不少了, 基本上就只包含源地址, 目的地址, 長度, 校驗, 數據。

交互過程也再也不像TCP這樣通過很複雜的創建鏈接和關閉鏈接的過程了, 就直接每次都發送數據了, 這樣會有以下的一些問題:

  1. 發送端只管發送數據, 若是在茫茫路由中該包丟了, 接收端並不知道
  2. 發送的多個包中, 在通過不一樣路由的時候, 可能達到時序跟發送的時候並不同, 因此接收端可能拿到的是不一樣順序的包
  3. 若是發送端很快, 而接收端很慢, 接收端處理不過來, 就會丟包

因此如前面所說, UDP協議並不保證數據的可靠性, 他通常用於一些高性能的場景, 且須要應用層再作一些簡單的封裝處理。

參考

    1. Linux C編程一站式學習. http://docs.linuxtone.org/ebooks/C&CPP/c/index.html
相關文章
相關標籤/搜索