通过网络工具查看redis客户端和服务器的交互过程

终于要开始阅读客户端和服务器的代码了,因为是整个程序的逻辑大框架,
所以不采用之前顺序阅读的方式,改用按照程序逻辑进行阅读,
所以需要熟悉一些工具来做支撑,下面是为了支撑做的准备实验。

实验内容:通过网络工具查看redis客户端和服务器的交互过程

准备两台机器
redis client 10.100.13.81
redis server 10.100.13.88

0.启动redis服务器
./redis-server  redis.conf

1.进行抓包(可以在服务端或者客户端,我们选择在客户端)

一开始的时候,我们使用的如下格式抓包,但是出现了丢包现象,经过查询,网上都推荐使用 -n -X -tt 方式抓包,不容易丢包
[root@localhost tmp]# tcpdump -w /tmp/c.cap  -i eno16777984 host 10.100.13.88 -s0
tcpdump: listening on eno16777984, link-type EN10MB (Ethernet), capture size 65535 bytes
^C147 packets captured
215 packets received by filter
68 packets dropped by kernel

-n     Don't convert host addresses to names.  This can be used to avoid DNS lookups.  
这个减少DNS查询比较有用
-tt    Print an unformatted timestamp on each dump line.
不转化时间格式
-s     Snarf  snaplen  bytes of data from each packet rather than the default of 65535 bytes.  
       Packets truncated because of a limited snapshot are indicated in the output with ``[|proto]'', 
       where proto is the name of the protocol level at which the truncation has occurred.  
       Note  that  taking  larger  snapshots both  increases the amount of time it takes to process packets and, 
       effectively, decreases the amount of packet buffering.  This may cause packets to be lost.  
       You should limit snaplen to the smallest number that will capture the protocol information you're interested in.  
       Setting snaplen to 0 sets it to the default of 65535, 
       for backwards compatibility with recent older versions of tcpdump.
-s0保存完整的数据包

所以正式抓包用的是如下格式: 我们的网卡是虚拟网卡,所以需要指定具体的名字
tcpdump -w /tmp/x.cap  -i eno16777984 host 10.100.13.88 and port 6379 -s0  -n -X -tt 
host 10.100.13.88  跟主机88的来往数据包,端口是6379,因为本地接口暂时未知,需要连接上才只知道
如果变成这样dst port 6379 就是单向了,目标端口是6379,只捕获发出去的报,src 表示源, 不写方向默认表示双向

       
2启动客户端
./redis-cli  -h 10.100.13.88

在tcpdump中读取显示保存的cap(需要对比tcp协议才能逐段看懂,这里我们采用拖到windows系统下的wireshark看)
tcpdump -r /tmp/x.cap -n -nn -A -x| vim -
-A     Print each packet (minus its link level header) in ASCII.  Handy for capturing web pages.
-n     Don't convert host addresses to names.  This can be used to avoid DNS lookups.
-nn    Don't convert protocol and port numbers etc. to names either.
-x     When  parsing and printing, in addition to printing the headers of each packet, 
       print the data of each packet (minus its link level header) in hex.  
       The smaller of the entire packet or snaplen bytes will be printed.  
       Note that this is the entire link-layer packet, 
       so for link layers that pad (e.g. Ether‐net), 
       the padding bytes will also be printed when the higher layer packet is shorter than the required padding.

vim - 从标准输入读取


3用wireshark查看cap文件
序列号  时间       源地址         目标地址        协议  长度  Unixtime(1970.1.1) 源口->目标口
"1",    "0.000000","10.100.13.81","10.100.13.88","TCP","74","1635383014.591000000","37170→6379 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=2288259946 TSecr=0 WS=128"
我们可以看到 [SYN] Seq=0 请求序列号是从0开始的,因为wireshark帮助我们处理过了(实际上是seq 1073727839),所以看起来赏心悦目
这里是建立tcp连接的第一次握手,客户端的请求SYN
"2",    "0.000169","10.100.13.88","10.100.13.81","TCP","74","1635383014.591169000","6379→37170 [SYN, ACK] Seq=0 Ack=1 Win=14480 Len=0 MSS=1460 SACK_PERM=1 TSval=2349079516 TSecr=2288259946 WS=128"
这里是建立tcp连接的第二次握手,服务端的回复和请求 ACK,SYN
"3",    "0.000210","10.100.13.81","10.100.13.88","TCP","66","1635383014.591210000","37170→6379 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=2288259946 TSecr=2349079516"
这里是建立tcp连接的第三次握手,客户端的回复 ACK
(这里需要注意,如果想在tcpdump中观察绝对的seq,记得添加符号大S -S,否则第三个ack开始也是相对序列号
-S     Print absolute, rather than relative, TCP sequence numbers.)

下面开始传数据
"4",    "0.000534","10.100.13.81","10.100.13.88","TCP","83","1635383014.591534000","37170→6379 [PSH, ACK] Seq=1 Ack=1 Win=29312 Len=17 TSval=2288259946 TSecr=2349079516"
观察数据区域,可以看到如下的数据
*1
$7
COMMAND
客户端向服务器发送了一个COMMAND命令,这样服务器会返回所有的命令信息,我们这里共204个,
目的是为了补全客户端这边落后的命令帮忙内容
具体的代码是在文件redis-cli.c中的 main->repl->cliIntegrateHelp

************************cli main *************************
.....
 /* Start interactive mode when no command is provided */
    if (argc == 0 && !config.eval) {
        printf("Start interactive mode when no command is provided!\n");
        /* Ignore SIGPIPE in interactive mode to force a reconnect */
        signal(SIGPIPE, SIG_IGN);

        /* Note that in repl mode we don't abort on connection error.
         * A new attempt will be performed for every command send. */
        cliConnect(0);
        repl();  这里
    }
************************cli main *************************

************************repl *************************
static void repl(void) {
    sds historyfile = NULL;
    int history = 0;
    char *line;
    int argc;
    sds *argv;

    /* Initialize the help and, if possible, use the COMMAND command in order
     * to retrieve missing entries. */
    cliInitHelp();
    cliIntegrateHelp(); 这里

    config.interactive = 1;
    。。。。。。
************************repl *************************

************************cliIntegrateHelp *************************
static void cliIntegrateHelp(void) {
    if (cliConnect(CC_QUIET) == REDIS_ERR) return;

    redisReply *reply = redisCommand(context, "COMMAND"); 就是这个语句向服务器发送了COMMAND命令
    if(reply == NULL || reply->type != REDIS_REPLY_ARRAY) return;
    ......
************************cliIntegrateHelp *************************

服务器回复的数据:
"6",    "0.001054","10.100.13.88","10.100.13.81","TCP","92","1635383014.592054000","6379→37170 [PSH, ACK] Seq=1 Ack=18 Win=14592 Len=26 TSval=2349079517 TSecr=2288259946"
。。。。。。
"754",    "0.006350","10.100.13.88","10.100.13.81","TCP","100","1635383014.597350000","6379→37170 [PSH, ACK] Seq=18502 Ack=18 Win=14592 Len=34 TSval=2349079523 TSecr=2288259952"
这些全部是服务器回复给客户端的命令详情
可用wireshark的跟踪流->tcpdump流查看,具体如下:
***********************************************
*204  共204个命令
*7    一个命令包含7个元素
$6
setbit
:4
*2
+write
+denyoom
:1
:1
:1
*3
+@write
+@bitmap
+@slow
..........
*7
$12
bgrewriteaof
:1
*2
+admin
+noscript
:0
:0
:0
*3
+@admin
+@slow
+@dangerous
***********************************************
我们又发送了一个ping命令给服务器
"756",    "14.260916","10.100.13.81","10.100.13.88","TCP","80","1635383028.851916000","37170→6379 [PSH, ACK] Seq=18 Ack=18536 Win=29312 Len=14 TSval=2288274206 TSecr=2349079523"
*1
$4
ping
服务器回复我们一个pong
"757",    "14.261053","10.100.13.88","10.100.13.81","TCP","73","1635383028.852053000","6379→37170 [PSH, ACK] Seq=18536 Ack=32 Win=14592 Len=7 TSval=2349093777 TSecr=2288274206"
+PONG
最后我们退出了客户端
"759",    "15.610126","10.100.13.81","10.100.13.88","TCP","66","1635383030.201126000","37170→6379 [FIN, ACK] Seq=32 Ack=18543 Win=29312 Len=0 TSval=2288275556 TSecr=2349093777"
客户端发起第一次挥手,发送结束请求FIN,这里的ACK是回复上述的PONG,确认收到,一个报文干了两件事
"760",    "15.610322","10.100.13.88","10.100.13.81","TCP","66","1635383030.201322000","6379→37170 [FIN, ACK] Seq=18543 Ack=33 Win=14592 Len=0 TSval=2349095127 TSecr=2288275556"
服务端发起第二和三次挥手,确认ACK客户端发送的结束请求,发送结束请求FIN给客户端,把两步合为一步
"761",    "15.610340","10.100.13.81","10.100.13.88","TCP","66","1635383030.201340000","37170→6379 [ACK] Seq=33 Ack=18544 Win=29312 Len=0 TSval=2288275556 TSecr=2349095127"
客户端发起第四次挥手,确认ACK服务端发送的结束请求

整个交互过程结束。

通过这个过程我们清晰的看到,REDIS客户端起来默认补全命令的动作,如果我们主动发送的ping请求,服务器的返回信息。
,在这个过程中也顺便复习了TCP的三次握手和四次挥手,为我们后续阅读调试redis的代码打下了坚实的基础。

 

上一篇:Wireshark数据抓包分析之传输层协议(TCP协议)


下一篇:【百度地图API】如何制作一张魔兽地图!!——CS地图也可以,哈哈哈