最近在学习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