tcp全名是傳輸控制協議,tcp協議在ip協議基礎上增長了數據包完整性檢查等保證傳輸完整性的機制,使其在如今的數據領域獲得了普遍的應用html
按照下面的步驟能夠快速瞭解tcp數據包中包含的信息git
rfc參考:tools.ietf.org/html/rfc793github
核心tcp數據包結構以下 bash
其中經常使用字段以下app
字段 | 做用 |
---|---|
Source Port | 發包機器的端口號 |
Destination Port | 收包機器的端口號 |
Sequence Number | 包編號 |
Acknowledgment Number | 確認包號 |
urg/ack/psh/rst/syn/fin | 標誌位,設置是/否的操做標誌 |
Window | 流量控制窗口 |
Checksum | 包完整性校驗 |
注意:客戶端和服務端使用獨立的包編號計數器器 checksum服務端和客戶端會分別計算,客戶端依靠這個值判斷tcp包在傳輸過程當中是否被異常改變/篡改tcp
可使用wireshark獲取一個tcp數據包,在tcp層單擊右鍵->複製->as a Hex Stream便可 ui
這裏獲得的tcp層包數據以下google
1f90f04f3747d146dcae23f3801831bf14ef00000101080a3176450b31764503
複製代碼
下面就能夠編寫程序從這個tcp的16進制中解析出報文的具體數據了spa
二進制數字位數和16進制位數換算關係:1個16進制數能夠表示4個二進制數 好比二進制:00011111 10010000 可使用16進製表示爲:1f90code
可使用下面的代碼將16進制轉成二進制字符串
func hex2bin(hex string) string {
var bin string
for i := 0; i < len(hex); i++ {
hex2int, _ := strconv.ParseInt(string(hex[i]), 16, 64)
bin = bin + fmt.Sprintf("%04b", hex2int)
}
return bin
}
複製代碼
而後就能夠按二進制位讀取tcp數據報信息了,參考代碼以下
func main() {
atcp := "1f90f04f3747d146dcae23f3801831bf14ef00000101080a3176450b31764503"
bintcp := hex2bin(atcp)
sourcePort, _ := strconv.ParseInt(bintcp[0:16], 2, 64)
fmt.Printf("sourcePort is %d \n", sourcePort)
destinationPort, _ := strconv.ParseInt(bintcp[16:32], 2, 64)
fmt.Printf("destinationPort is %d \n", destinationPort)
sequenceNumber, _ := strconv.ParseInt(bintcp[32:64], 2, 64)
fmt.Printf("sequenceNumber is %d \n", sequenceNumber)
acknowledgmentNumber, _ := strconv.ParseInt(bintcp[64:96], 2, 64)
fmt.Printf("acknowledgmentNumber is %d \n", acknowledgmentNumber)
dataOffset, _ := strconv.ParseInt(bintcp[96:100], 2, 64)
fmt.Printf("dataOffset is %d \n", dataOffset)
reserved, _ := strconv.ParseInt(bintcp[100:106], 2, 64)
fmt.Printf("reserved is %d \n", reserved)
// Control Bits 控制位,從106-1012共有6位,每位表示一個控制位的開關
urg, _ := strconv.ParseInt(bintcp[106:107], 2, 64)
ack, _ := strconv.ParseInt(bintcp[107:108], 2, 64)
psh, _ := strconv.ParseInt(bintcp[108:109], 2, 64)
rst, _ := strconv.ParseInt(bintcp[109:110], 2, 64)
syn, _ := strconv.ParseInt(bintcp[110:111], 2, 64)
fin, _ := strconv.ParseInt(bintcp[111:112], 2, 64)
fmt.Printf("控制位標識以下:\n")
fmt.Printf(" urg: %d\n", urg)
fmt.Printf(" ack: %d\n", ack)
fmt.Printf(" psh: %d\n", psh)
fmt.Printf(" rst: %d\n", rst)
fmt.Printf(" syn: %d\n", syn)
fmt.Printf(" fin: %d\n", fin)
// 數據窗口 16位
window, _ := strconv.ParseInt(bintcp[112:128], 2, 64)
fmt.Printf("window is %d \n", window)
// checksum 16位
checksum, _ := strconv.ParseInt(bintcp[128:144], 2, 64)
fmt.Printf("checksum is %d \n", checksum)
// urgentPointer
urgentPointer, _ := strconv.ParseInt(bintcp[144:160], 2, 64)
fmt.Printf("urgentPointer is %d \n", urgentPointer)
// options and padding
optionsAndPaddings := bintcp[160:]
fmt.Printf("optionsAndPaddings is %s \n", optionsAndPaddings)
fmt.Printf("tcp raw data is %s \n", atcp)
fmt.Printf("tcp bin data is %s \n", bintcp)
fmt.Printf("tcp bin data length is %d\n", len(bintcp))
}
複製代碼
執行效果以下
wireshark解析結果以下
能夠看到解析是ok的
wireshark中複製的tcp包數據是16進制的,可是tcp協議中,部分字段僅佔用一位,16進制是2進制的4的整數倍,直接解析16進制會致使tcp中的某些字段沒法獲取,須要先轉成二進制進行處理
tcp包數據最終會進行32位對齊,整個tcp數據包大小若是不是正好是32位長度的整數倍,會用0在末尾填充到32位整數倍
tcp協議在rfc3168中新增了cwr和ece標誌位,能夠參考:tools.ietf.org/html/rfc316…
抓包可使用tcpdump: tcpdump -n -XX -i lo0 -s0 'tcp port 8080'