今天踩了redis的一个大坑。就是关于 RESP 协议的。
RESP
RESP 本身基于 TCP 双工通信,在应用层采取的就是 RESP 协议进行数据交互。协议本身内容非常简单。
发送请求:SET key value
*3
$3
SET
$3
key
$5
value
- 开头固定
* number
,number 表示:命令 + 参数,一共有多少个部分。比如上述案例的*3表示有3部分——①SET②key③value - 每部分开头就是
$ number
,number表示:该部分有多少个字符。比如value部分的前面的$5
,就表示该部分有5个字符。
但是实际上发往 redis 的内容实际上是:
*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue
注意大坑
这里有个大坑:每一部分的换行符固定是\r\n
,而非\n
。
实践证明:这个地方固定式\r\n
,而不依赖于具体的操作系统。
亲手测试:
linux 下,vim 一个 java 脚本,写入:
import java.io.OutputStream; import java.net.Socket; import java.nio.charset.StandardCharsets; /** * @author yq * @version v1.0 2023-01-19 9:28 PM */ public class TestMain { public static void main(String[] args) { try { Socket socket = new Socket("192.168.204.140", 6379); OutputStream os = socket.getOutputStream(); os.write("*3\n$3\nSET\n$7\nT-WYQ-T\n$3\n323\n".getBytes(StandardCharsets.UTF_8)); os.flush(); socket.close(); }catch (Throwable e) { e.printStackTrace(); } } }
注意中间换行符是
\n
,然后javac编译,然后java命令跑一把,把命令用socket发出去了。这样再开一个 ssh 窗口,然后
nc 192.168.204.140(自己的虚拟机) 6379
,然后直接GET T-WYQ-T
,发现结果是-1。然后把换行符换成
\r\n
,重新重复上述操作,就发现能GET到了!其他证明
查看 jedis 源码,发现源码写死了,固定就是CRLF。(无论哪个系统,jar包里面肯定是写死的)