通過抓包分析 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

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