协议是指两个实体(群)之间通信的约定标准,本文主要探讨Redis服务端和客户端交互的协议,其余集群相关的协议留待以后探讨。 协议算是一个必要而伟大的发明。A和B语言不通,但他们可以通过彼此都熟知的方式交流,比如肢体语言,肢体语言在这里就是协议。语言只是交流的一种表现形式,真正的交流与语言无关,就像最早翻译英文的神人,猜想也是用上了肢体语言的吧。除了Redis协议,语言翻译,还有诸如食谱、药方、API都是协议的表现形式,甚至编程语言也可以理解为程序员与机器之间的协议(当然有编译过程)。协议是一种规范,是为了通信双方能在同样的语境下交流,能看得懂,不出错。
Redis协议是基于TCP的,最后都是字节流,一些控制字符加上数据,就是整个协议。(编程语言是关键字加上代码)
首先来看看Redis协议里的对象类型: (1)简单字符串,首字符是‘+’,其后是字符串内容 (2)错误,首字符是'-',其后是错误信息 (3)整数,首字符是':',其后是一个整数 (4)定长字符串,首字符是'$',其后是字符串长度,下面跟字符串,特殊的可以用 "$-1/r/n" 来表示来表示NULL (5)数组,首字符是' ',其后是数据长度,下面是数组内容 Redis客户端和服务端通信都基于这五种基本对象,客户端向服务端只会发送定长字符串数据,服务端会根据命令执行结果返回上面的对象类型。比如一个简单的SET命令 "SET a 12",表示成Redis协议如下:" 3/r/n$3/r/nSET/r/n$1/r/na/r/n$2/r/n12/r/n",其中 "/r/n" Redis用来分隔每个对象,上传命令服务端的回复是 "+OK/r/n",表示命令执行成功。
其它的命令可以用tcpdump抓包并用wireshark分析,tcpdump命令是: sudo tcpdump -nn -i lo0 port 6379 -w wire.cap
然后用wireshark分析包即可,强烈建议实际动手操作一下,对协议的理解大有好处。
协议搞清楚了,下面简单实验一下,调用一下“SET a 12”这个命令。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int cPort = 6379; int cClient = 0; int cLen = 0; struct sockaddr_in cli; char cbuf[4096] = {0}; memset(cbuf, 0, sizeof(cbuf)); cli.sin_family = AF_INET; cli.sin_port = htons(cPort); cli.sin_addr.s_addr = inet_addr("127.0.0.1"); cClient = socket(AF_INET, SOCK_STREAM, 0); if (cClient < 0) { printf("socket() failure!/n"); return -1; } if (connect(cClient, (struct sockaddr*)&cli, sizeof(cli)) < 0) { printf("connect() failure!/n"); return -1; } //send command char command[] = "*3/r/n$3/r/nSET/r/n$1/r/na/r/n$2/r/n12/r/n"; if (send(cClient, command, sizeof(command), 0) == -1) { printf("send failure/n"); return -1; } cLen = recv(cClient, cbuf, sizeof(cbuf), 0); if ((cLen < 0) || (cLen == 0)) { printf("recv() failure!/n"); return -1; } printf("recv() Data From Server: [%s]/n", cbuf); close(cClient); return 0; }
目前正在着手Redis系列文章,设计Redis基本原理、使用、运维、底层架构,大家有感兴趣的话题,可以联系我