1. 程式人生 > >1.2 TCP首部格式

1.2 TCP首部格式

  TCP是一個通訊協議,所謂協議就是通訊各方約定好的通訊規則。大家必須全部嚴格遵守這些規則,通訊才能順利完成。TCP的通訊規則由一系列RFC來規範。首先是首部格式。

在Linux中對應的結構體為:

 include/uapi/linux/tcp.h
 24 struct tcphdr {
 25     __be16  source;
 26     __be16  dest;
 27     __be32  seq;
 28     __be32  ack_seq;      
 29 #if defined(__LITTLE_ENDIAN_BITFIELD)
 30     __u16   res1:4,
 31         doff:4,
 32         fin:1,
 33         syn:1,
 34         rst:1,
 35         psh:1,
 36         ack:1,
 37         urg:1,
 38         ece:1,
 39         cwr:1;
 40 #elif defined(__BIG_ENDIAN_BITFIELD)
 41     __u16   doff:4,
 42         res1:4,
 43         cwr:1,
 44         ece:1,
 45         urg:1,
 46         ack:1,
 47         psh:1,
 48         rst:1,
 49         syn:1,
 50         fin:1;
 51 #else
 52 #error  "Adjust your <asm/byteorder.h> defines"
 53 #endif  
 54     __be16  window;
 55     __sum16 check;
 56     __be16  urg_ptr;
 57 };

欄位說明:
  source:源埠號,取值範圍1-65535;

  dest:目的埠號,取值範圍1-65535;
     seq:在SYN位元沒有設定的情況下,seq是報文段中第一個位元組的編號;
     ack_seq:在ACK位元設定的情況下,ack_seq是這個報文的傳送者希望收到的下一個seq號;

  下面的16bit使用了位域,由於機器由於“大端序”與“小端序”的不同,讀取和儲存每個位元的順序是相反的,故需要用條件編譯來區分。
     doff:TCP頭的大小,其數值乘4就是TCP頭的位元組數;
     Reserved:  留待將來使用,必須為0;
     Control Bits:
         CWR:Congestion Window Reduced,用於擁塞控制;
         ECE:ECN-Echo,用於擁塞控制;
         URG:緊急指標域有效;
         ACK:確認號域有效;
         PSH:推送功能,TCP收到此標識應該將資料立即提交接收者;如在GRO處理流程中,如果發現此標記則不快取包,直接交付協議棧;
         RST:重置連線;
         SYN:同步序列號,TCP通過設定此標誌位告知對端起始序列號;
         FIN:送端不再有資料傳送;
     視窗:報文的傳送者願意接收的從ack_seq開始的資料的位元組數;
     檢驗和:用TCP首部、資料部分和偽首部計算的檢驗和。偽首部由源IP地址、目的IP地址、協議號、TCP首部+資料的位元組數構成;
     緊急指標:表示緊急資料在此報文資料段中的偏移,即此報文的seq加上緊急指標的值就得到緊急資料的首序列號。這個欄位僅在URG控制位元設定時才有效。

  選項:doff乘4再減去TCP基本首部長度20位元組即為選項欄位長度。選項用於擴充套件TCP的功能,不是必須。比較重要的選項有最大報文段、視窗擴大、時間戳、選擇確認。

  使用struct tcphdr解析或構建TCP報文很簡單,只要定義一個struct tcphdr結構體指標,將其指向TCP報文段的首地址,然後就可以對各個欄位進行讀寫了。

  要特別說明的是,seq和ack_seq都是無符號32位的整數。同一端的seq和ack_seq之間是獨立的,無任何聯絡,但接收端的ack_seq與傳送端的seq是同一空間。seq的取值範圍為0-4294967295,初始的值是隨機選取的,每傳送一個位元組的資料就消耗一個seq,SYN標誌位和FIN標誌位也會消耗一個seq。如果資料傳送很快,seq就會很快回繞,這時TCP可能無法區分舊資料與新資料,這樣就得通過時間戳選項等非法來解決這一問題(時間戳選項是後話)。

  TCP協議自1981年由RFC 793提出到現在已經30多年了,其首部只是增加了CWR和ECE兩個標記,以及增加了若干選項欄位,現在仍然運轉良好。相比之下IP協議就不得不傷筋動骨地向IPv6過渡,就不得不佩服當初協議設計者的遠見卓識啊!

  TCP首部欄位只是核心使用,通過socket API使用TCP的使用者是接觸不到的(除非使用AF_PACKET socket或RAW socket收發網路資料)。下面我們瞭解一下用socket API時TCP的基本使用方式。