通过抓包分析 redis 通信协议, 我发现我也能实现一个 redis 客户端

概述

redis 客户端与服务端通信的本质就是基于 socket 的网络编程, 通过字节流来传输数据, 在将数据转成字节流之前, 客户端需要将待传输的数据按照具体的通信协议格式组装一下, 本文主要来分析的是 redis 客户端是通过什么样的数据格式从服务端 读写数据 的.

插入一条数据

如果往 redis 中插入一条 key=username, value=zhangsan的数据, 命令如下:

SET USERNAME ZHANGSAN

实际的通信数据(转为字节之前), 如下所示:

// 原始字符串
*3\r\n$3\r\nSET\r\n$8 \r\nUSERNAME\r\n$8\r\nZHANGSAN

// 为了方便, 格式化如下  
*3
$3
SET
$8
USERNAME
$8
ZHANGSAN

协议格式说明

  • 为了形象表示, redis 客户端传输的数据格式由 N 多个 组成, 每个 由星号(*) 或者 美元符号($)开头, 回车换行符(\r\n)结尾, 中间是不同含义的数据.

  • 以星号开头的段称为 头段, 以美元符号开头的段成为 数据段.

  • 不管是 redis 操作指令(比如 set、 get 等), 还是指令后面跟的操作数, 统称为 参数, 比如set username zhangsan 这条命令中就包含了 3 个参数.

  • 头段中的数据位表示传输的 参数个数, 数据段中的数据位表示为 参数的长度 或者 具体的参数值.

示例如下:

用 Java 实现 redis 客户端

以下是测试代码:

public class App {
    public static void main(String[] args) throws IOException {

        Socket socket = new Socket("localhost", 6379);
        OutputStream out = socket.getOutputStream();
        auth(out);
        set(out);
        //get(out);
        //lPush(out);
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        while (inputStream.read(buf)!=-1) {
            System.out.println(new String(buf));
        }
    }

  	// 认证 auth
    public static void auth(OutputStream os) throws IOException {
        String s = "*2\r\n$4\r\nAUTH\r\n$11\r\nQpLpYnh619!\r\n";
        os.write(s.getBytes(StandardCharsets.UTF_8));
        os.flush();
    }

   // SET USERNAME ZHANGSAN
    public static void set(OutputStream os) throws IOException {
        String s = "*3\r\n$3\r\nSET\r\n$8\r\nUSERNAME\r\n$8\r\nZHANGSAN\r\n";
        os.write(s.getBytes(StandardCharsets.UTF_8));
        os.flush();
    }
		
  	// GET USERNAME
    public static void get(OutputStream os) throws IOException {
        String s = "*2\r\n$3\r\nGET\r\n$8\r\nUSERNAME\r\n";
        os.write(s.getBytes(StandardCharsets.UTF_8));
        os.flush();
    }

	  // LPUSH food apple
    public static void lPush(OutputStream os) throws IOException {
        String s = "*3\r\n$5\r\nLPUSH\r\n$4\r\nfood\r\n$5\r\napple\r\n";
        os.write(s.getBytes(StandardCharsets.UTF_8));
        os.flush();
    }
  
    // 其他
 
}

关于抓包

实践过程中, 是用 tcpdump 命令 + wireshark 软件进行抓包分析. 两者的安装及详细使用方法, 请自行了解.

  1. 通过 tcpdump 命令抓取 redis 服务端所在机器的网络数据包
// 抓取 eth0 网卡, 端口为 6379 的数据, 然后保存到 redis_6379.pcap 文件下.
tcpdump port 6379 -i eth0 -w redis_6379.pcap
  1. 安装 wireshark 及解析 redis 协议的插件

    可以参考 这篇教程

  2. 将 redis_6379.pcap 文件转移到本机, 用 wireshark 软件打开.

  1. 右键 follw -> TCP Stream

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章