最近在學習rpcx,爲了更清楚搞懂rpcx的各類協議組成。對rpcx的協議作了一個深刻學習。html
系統: mac OS 10.14.3
golang: go1.12 darwin/amd64
調試工具:lldb
抓包工具:tcpdumplinux
# (一)tcpdump工具使用
tcpdump是一個網絡抓包工具,tcpdump支持針對網絡層、協議、主機、網絡或端口的過濾。
並提供and、or、not等邏輯語句來幫助你去掉無用的信息。
tcpdump格式信息以下:
參數詳解git
-a -- 將網絡地址和廣播地址轉變成名字 -d -- 將匹配信息包的代碼以人們可以理解的彙編格式給出 -dd -- 將匹配信息包的代碼以c語言程序段的格式給出 -ddd -- 將匹配信息包的代碼以十進制的形式給出 -e -- 在輸出行打印出數據鏈路層的頭部信息 -f -- 將外部的Internet地址以數字的形式打印出來 -l -- 使標準輸出變爲緩衝行形式 -n -- 不把網絡地址轉換成名字 -t -- 在輸出的每一行不打印時間戳 -v -- 輸出一個稍微詳細的信息,例如在ip包中能夠包括ttl和服務類型的信息 -vv -- 輸出詳細的報文信息 -c -- 在收到指定的包的數目後,tcpdump就會中止 -F -- 從指定的文件中讀取表達式,忽略其它的表達式 -i -- 指定監聽的網絡接口 -r -- 從指定的文件中讀取包(這些包通常經過-w選項產生) -w -- 直接將包寫入文件中,並不分析和打印出來 -T -- 將監聽到的包直接解釋爲指定的類型的報文,常見的類型有rpc (遠程過程調用)和snmp(簡單網絡管理協議) -S -- 打印TCP 數據包的順序號時, 使用絕對的順序號, 而不是相對的順序號. -XX -- 當分析和打印時, tcpdump 會打印每一個包的頭部數據, 同時會以16進制和ASCII碼形式打印出每一個包的數據, 其中包括數據鏈路層的頭部.這對於分析一些新協議的數據包很方便.
命令:github
1)想要截獲全部192.168.1.100 的主機收到的和發出的全部的數據包: tcpdump host 192.168.1.100 2)想要截獲主機192.168.1.100 和主機192.168.1.101 或192.168.1.102的通訊,使用命令:(在命令行中使用 括號時,必定要轉義) tcpdump host 192.168.1.100 and (192.168.1.101 or 192.168.1.102) 3)若是想要獲取主機192.168.1.100除了和主機192.168.1.101以外全部主機通訊的ip包,使用命令: tcpdump ip host 192.168.1.100 and ! 192.168.1.101 4)若是想要獲取主機192.168.1.100接收或發出的telnet包,使用以下命令: tcpdump tcp port 23 and host 192.168.1.100 5)獲取迴環網卡8972端口包,例如迴環網卡爲lo0,(unix/linux)這個可使用ifconfig查看,使用命令以下: tcpdump -i lo0 port 8972 6)獲取迴環網卡8972端口包,順序打印並打印每一個包16進制數據 tcpdump -i lo0 port 8972 -S -XX
源碼
https://github.com/rpcx-ecosystem/rpcx-examples3/tree/master/101basicgolang
編譯server go build -o server server.go 編譯client go build -o client_async client/client_async.go
## 2.2第二步運行tcpdump:
(1)啓動server、啓動對應的server文件
(2)運行tcpdump編程
tcpdump -i lo0 port 8972 -S -XX
rpcx例子裏面默認啓動8972端口,因爲是本地去測試,我抓的是lo0網卡,lo0網卡爲迴環網卡,
能夠經過ifconfig查看到。
windows
(3)執行client_async
執行client_async,tcpdump抓包信息以下:bash
14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 0,sackOK,eol], length 0 0x0000: 0200 0000 4500 0040 0000 4000 4006 0000 ....E..@..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b711 ..........#..... 0x0020: 0000 0000 b002 ffff fe34 0000 0204 3fd8 .........4....?. 0x0030: 0103 0306 0101 080a 184f 4ca7 0000 0000 .........OL..... 0x0040: 0402 0000 .... 14:17:03.977157 IP localhost.8972 > localhost.58526: Flags [S.], seq 2001029784, ack 3652826898, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 407850151,sackOK,eol], length 0 0x0000: 0200 0000 4500 0040 0000 4000 4006 0000 ....E..@..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4a98 ........#...wEJ. 0x0020: d9b9 b712 b012 ffff fe34 0000 0204 3fd8 .........4....?. 0x0030: 0103 0306 0101 080a 184f 4ca7 184f 4ca7 .........OL..OL. 0x0040: 0402 0000 .... 14:17:03.977177 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029785, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b712 ..........#..... 0x0020: 7745 4a99 8010 18eb fe28 0000 0101 080a wEJ......(...... 0x0030: 184f 4ca7 184f 4ca7 .OL..OL. 14:17:03.977184 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826898, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4a99 ........#...wEJ. 0x0020: d9b9 b712 8010 18eb fe28 0000 0101 080a .........(...... 0x0030: 184f 4ca7 184f 4ca7 .OL..OL. 14:17:03.977425 IP localhost.58526 > localhost.8972: Flags [P.], seq 3652826898:3652826961, ack 2001029785, win 6379, options [nop,nop,TS val 407850151 ecr 407850151], length 63 0x0000: 0200 0000 4500 0073 0000 4000 4006 0000 ....E..s..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b712 ..........#..... 0x0020: 7745 4a99 8018 18eb fe67 0000 0101 080a wEJ......g...... 0x0030: 184f 4ca7 184f 4ca7 0800 0030 0000 0000 .OL..OL....0.... 0x0040: 0000 0000 0000 002f 0000 0005 4172 6974 ......./....Arit 0x0050: 6800 0000 034d 756c 0000 0000 0000 0017 h....Mul........ 0x0060: 82a1 41d3 0000 0000 0000 000a a142 d300 ..A..........B.. 0x0070: 0000 0000 0000 14 ....... 14:17:03.977440 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826961, win 6378, options [nop,nop,TS val 407850151 ecr 407850151], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4a99 ........#...wEJ. 0x0020: d9b9 b751 8010 18ea fe28 0000 0101 080a ...Q.....(...... 0x0030: 184f 4ca7 184f 4ca7 .OL..OL. 14:17:03.978268 IP localhost.8972 > localhost.58526: Flags [P.], seq 2001029785:2001029837, ack 3652826961, win 6378, options [nop,nop,TS val 407850152 ecr 407850151], length 52 0x0000: 0200 0000 4500 0068 0000 4000 4006 0000 ....E..h..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4a99 ........#...wEJ. 0x0020: d9b9 b751 8018 18ea fe5c 0000 0101 080a ...Q.....\...... 0x0030: 184f 4ca8 184f 4ca7 0800 8030 0000 0000 .OL..OL....0.... 0x0040: 0000 0000 0000 0024 0000 0005 4172 6974 .......$....Arit 0x0050: 6800 0000 034d 756c 0000 0000 0000 000c h....Mul........ 0x0060: 81a1 43d3 0000 0000 0000 00c8 ..C......... 14:17:03.978283 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029837, win 6378, options [nop,nop,TS val 407850152 ecr 407850152], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b751 ..........#....Q 0x0020: 7745 4acd 8010 18ea fe28 0000 0101 080a wEJ......(...... 0x0030: 184f 4ca8 184f 4ca8 .OL..OL. 14:17:04.978001 IP localhost.58526 > localhost.8972: Flags [F.], seq 3652826961, ack 2001029837, win 6378, options [nop,nop,TS val 407851148 ecr 407850152], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b751 ..........#....Q 0x0020: 7745 4acd 8011 18ea fe28 0000 0101 080a wEJ......(...... 0x0030: 184f 508c 184f 4ca8 .OP..OL. 14:17:04.978053 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826962, win 6378, options [nop,nop,TS val 407851148 ecr 407851148], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4acd ........#...wEJ. 0x0020: d9b9 b752 8010 18ea fe28 0000 0101 080a ...R.....(...... 0x0030: 184f 508c 184f 508c .OP..OP. 14:17:04.978236 IP localhost.8972 > localhost.58526: Flags [F.], seq 2001029837, ack 3652826962, win 6378, options [nop,nop,TS val 407851149 ecr 407851148], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 230c e49e 7745 4acd ........#...wEJ. 0x0020: d9b9 b752 8011 18ea fe28 0000 0101 080a ...R.....(...... 0x0030: 184f 508d 184f 508c .OP..OP. 14:17:04.978291 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029838, win 6378, options [nop,nop,TS val 407851149 ecr 407851149], length 0 0x0000: 0200 0000 4500 0034 0000 4000 4006 0000 ....E..4..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b752 ..........#....R 0x0020: 7745 4ace 8010 18ea fe28 0000 0101 080a wEJ......(...... 0x0030: 184f 508d 184f 508d .OP..OP.
咱們發現對應抓包的信息有12個包,每一個包之間16進制數據有不少都是相同的。網絡
對應數據包數據結構
14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535, options [mss 16344,nop,wscale 6,nop,nop,TS val 407850151 ecr 0,sackOK,eol], length 0 0x0000: 0200 0000 4500 0040 0000 4000 4006 0000 ....E..@..@.@... 0x0010: 7f00 0001 7f00 0001 e49e 230c d9b9 b711 ..........#..... 0x0020: 0000 0000 b002 ffff fe34 0000 0204 3fd8 .........4....?. 0x0030: 0103 0306 0101 080a 184f 4ca7 0000 0000 .........OL..... 0x0040: 0402 0000
網絡傳輸中的字節碼都是大端讀取的,這個是爲了兼容CPU架構。因此在編程中須要轉換網絡字節碼,其實就是小端轉成大端。
就像咱們的02 00
完整解析能夠看以下圖:
MAC幀頭定義
/*數據幀定義,頭14個字節,尾4個字節*/ typedef struct _MAC_FRAME_HEADER { char m_cDstMacAddress[6]; //目的mac地址 char m_cSrcMacAddress[6]; //源mac地址 short m_cType; //上一層協議類型,如0x0800表明上一層是IP協議,0x0806爲arp }__attribute__((packed))MAC_FRAME_HEADER,*PMAC_FRAME_HEADER; typedef struct _MAC_FRAME_TAIL { unsigned int m_sCheckSum; //數據幀尾校驗和 }__attribute__((packed))MAC_FRAME_TAIL, *PMAC_FRAME_TAIL;
結合上圖咱們能夠看到貌似咱們並無使用到mac幀頭,圖中鏈路層只佔用4字節。這是由於咱們使用的是迴環鏈路。針對於鏈路層有不少的形式。不一樣的形式,鏈路層佔用的字節長度不一樣。
IP頭結構的定義
/*IP頭定義,共20個字節*/ typedef struct _IP_HEADER { char m_cVersionAndHeaderLen; //版本信息(前4位),頭長度(後4位) char m_cTypeOfService; // 服務類型8位 short m_sTotalLenOfPacket; //數據包長度 short m_sPacketID; //數據包標識 short m_sSliceinfo; //分片使用 char m_cTTL; //存活時間 char m_cTypeOfProtocol; //協議類型 short m_sCheckSum; //校驗和 unsigned int m_uiSourIp; //源ip unsigned int m_uiDestIp; //目的ip } __attribute__((packed))IP_HEADER, *PIP_HEADER ;
tcp頭結構定義
/*TCP頭定義,共20個字節*/ typedef struct _TCP_HEADER { short m_sSourPort; // 源端口號16bit short m_sDestPort; // 目的端口號16bit unsigned int m_uiSequNum; // 序列號32bit unsigned int m_uiAcknowledgeNum; // 確認號32bit short m_sHeaderLenAndFlag; // 前4位:TCP頭長度;中6位:保留;後6位:標誌位 short m_sWindowSize; // 窗口大小16bit short m_sCheckSum; // 檢驗和16bit short m_surgentPointer; // 緊急數據偏移量16bit }__attribute__((packed))TCP_HEADER, *PTCP_HEADER;
TCP頭中的選項定義
/*TCP頭中的選項定義 kind(8bit)+Length(8bit,整個選項的長度,包含前兩部分)+內容(若是有的話) KIND = 1表示 無操做NOP,無後面的部分 2表示 maximum segment 後面的LENGTH就是maximum segment選項的長度(以byte爲單位,1+1+內容部分長度) 3表示 windows scale 後面的LENGTH就是 windows scale選項的長度(以byte爲單位,1+1+內容部分長度) 4表示 SACK permitted LENGTH爲2,沒有內容部分 5表示這是一個SACK包 LENGTH爲2,沒有內容部分 8表示時間戳,LENGTH爲10,含8個字節的時間戳 */ typedef struct _TCP_OPTIONS { char m_ckind; char m_cLength; char m_cContext[32]; }__attribute__((packed))TCP_OPTIONS, *PTCP_OPTIONS;
圖中爲tcpdump源碼調試過程,若是走的是迴環鏈路, 且大家系統是mac os則回走null_if_print方法。
tcpdump中print-null.c源碼:
這個 DLT_NULL 數據包頭的長度爲4字節。它包含主機字節順序指定系列的32位整數,例如AF_INET。OpenBSD DLT_LOOP包頭是相同的,除了整數按網絡字節順序排列。
DLT_NULL是 BSD迴路封裝,因爲mac OS是BSD系類改造的。因此用的是同樣的。DLT_LOOP是OpenBSD的迴路封裝。
迴環網卡接口打印的代碼以下:
void null_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p) { u_int length = h->len; u_int caplen = h->caplen; uint32_t family; ndo->ndo_protocol = "null"; if (caplen < NULL_HDRLEN) { ndo->ndo_ll_header_length += caplen; nd_print_trunc(ndo); return; } ndo->ndo_ll_header_length += NULL_HDRLEN; family = GET_HE_U_4(p); //4字節表標識 if ((family & 0xFFFF0000) != 0) family = SWAPLONG(family); if (ndo->ndo_eflag) null_hdr_print(ndo, family, length); length -= NULL_HDRLEN; //tcp數據包長度 caplen -= NULL_HDRLEN; //總體包長度,包含鏈路層 p += NULL_HDRLEN; switch (family) { case BSD_AFNUM_INET: //ipv4 ip_print(ndo, p, length); //打印數據 break; case BSD_AFNUM_INET6_BSD: //ipv6 case BSD_AFNUM_INET6_FREEBSD: case BSD_AFNUM_INET6_DARWIN: ip6_print(ndo, p, length); //打印數據 break; case BSD_AFNUM_ISO: isoclns_print(ndo, p, length); break; case BSD_AFNUM_APPLETALK: atalk_print(ndo, p, length); break; case BSD_AFNUM_IPX: ipx_print(ndo, p, length); break; default: /* unknown AF_ value */ if (!ndo->ndo_eflag) null_hdr_print(ndo, family, length + NULL_HDRLEN); if (!ndo->ndo_suppress_default_print) ND_DEFAULTPRINT(p, caplen); } return; }
圖中調試爲caplen爲網絡4層協議的包大小5六、length爲數據包大小52。他們恰好相差4。
TCP/IP 三次握手 14:17:03.977088 IP localhost.58526 > localhost.8972: Flags [S], seq 3652826897, win 65535 14:17:03.977157 IP localhost.8972 > localhost.58526: Flags [S.], seq 2001029784, ack 3652826898 14:17:03.977177 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029785, win 6379 14:17:03.977184 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826898, win 6379 客戶端往服務端發送rpc獲取請求 14:17:03.977425 IP localhost.58526 > localhost.8972: Flags [P.], seq 3652826898:3652826961, ack 2001029785, win 6379 14:17:03.977440 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826961, win 6378 服務端往客戶端回覆rpc數據 14:17:03.978268 IP localhost.8972 > localhost.58526: Flags [P.], seq 2001029785:2001029837, ack 3652826961 14:17:03.978283 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029837, win 6378 TCP/IP 四次揮手 14:17:04.978001 IP localhost.58526 > localhost.8972: Flags [F.], seq 3652826961, ack 2001029837 14:17:04.978053 IP localhost.8972 > localhost.58526: Flags [.], ack 3652826962, win 6378 14:17:04.978236 IP localhost.8972 > localhost.58526: Flags [F.], seq 2001029837, ack 3652826962 14:17:04.978291 IP localhost.58526 > localhost.8972: Flags [.], ack 2001029838, win 6378
localhost.58526 客戶端
localhost.8972 服務端
從我麼的12次請求中能夠看到分別有幾個部分:
(1)TCP/IP 三次握手
(2)客戶端往服務端發送rpc獲取請求
(3)服務端往客戶端回覆rpc數據
(4)TCP/IP 四次揮手
注意:四次揮手並非每次都是這樣的順序,在雙工下。或者是fin延遲狀況下。4次揮手的順序會有所變化。
有興趣能夠去讀一下TCP/IP原理。
先看看源代碼:
server端:
package main import ( "flag" "fmt" "github.com/smallnest/rpcx/server" ) var ( addr = flag.String("addr", "0.0.0.0:8972", "server address") ) type Args struct { //接收數據 A int B int } type Reply struct { //回覆數據 C int } type Arith int //調用的方法 func (t *Arith) Mul(ctx context.Context, args *Args, reply *Reply) error { reply.C = args.A * args.B fmt.Printf("call: %d * %d = %d\n", args.A, args.B, reply.C) return nil } func main() { flag.Parse() s := server.NewServer() s.Register(new(Arith), "") s.Serve("tcp", *addr) }
client端:
package main import ( "context" "flag" "log" "time" example "github.com/rpcx-ecosystem/rpcx-examples3" "github.com/smallnest/rpcx/client" ) var ( addr2 = flag.String("addr", "127.0.0.1:8972", "server address") ) func main() { flag.Parse() d := client.NewPeer2PeerDiscovery("tcp@"+*addr2, "") xclient := client.NewXClient("Arith", client.Failtry, client.RandomSelect, d, client.DefaultOption) defer xclient.Close() args := &example.Args{ //發送數據 A: 10, B: 20, } reply := &example.Reply{} //返回數據 call, err := xclient.Go(context.Background(), "Mul", args, reply, nil) //調用Mul方法 if err != nil { log.Fatalf("failed to call: %v", err) } time.Sleep(1e9) replyCall := <-call.Done if replyCall.Error != nil { log.Fatalf("failed to call: %v", replyCall.Error) } else { log.Printf("%d * %d = %d", args.A, args.B, reply.C) } }
返回結果A(10) * B(20) = C(200)
對應rpcx消息體結構體,對應文件rpcx/protocol/message.go
type Message struct { *Header ServicePath string ServiceMethod string Metadata map[string]string //設置受權密碼時用到 Payload []byte data []byte }
魔術數字,對應文件rpcx/protocol/message.go
const ( magicNumber byte = 0x08 ) func MagicNumber() byte { return magicNumber }
client端request
server端Response
數據包:
1.tcp/ip的四次揮手並非4次的順序都是固定的。若是是雙工或者是延遲發送,則順序就不固定。
2.mac OS的鏈路層佔用4個字節;
3.cap包數據包大小爲鏈路層、網絡層、傳輸層、應用層4個部分。ip頭數據包大小爲網絡層、傳輸層、應用層3個部分。
4.鏈路層不一樣的網卡鏈路層佔用字節大小不同,以太網鏈路層爲MAC幀頭。
參考資料:
鏈路層類型
https://blog.csdn.net/shenwansangz/article/details/47612505
計算機網絡-數據結構-MAC幀頭-IP頭-TCP頭-UDP頭
https://www.cnblogs.com/cs-lcy/p/7462072.html
IP頭,TCP頭,UDP頭,MAC幀頭定義
https://www.cnblogs.com/li-hao/archive/2011/12/07/2279912.html