【Redis學習】redis通訊協議


服務是一個較抽象的概念,意在幫助使用者達到某種需求。那么對於網絡服務來說,通常我們可以將其定義為一個運行在操作系統上的程序,使用者通過網絡與其進行交互並得到想要的信息。

客戶端和服務器通過TCP連接來進行數據交互,所以它和其它網絡服務一樣有一個協議。redis服務器接受命令以及命令的參數。服務器會在接到命令之后對命令進行處理,並將命令的回復傳送到客戶端。具體在網絡層上就是:Redis在TCP端口6379上監聽到來的連接,客戶端連接到來時,Redis服務器為此創建一個TCP連接。在客戶端與服務器端之間傳輸的每個Redis命令或者數據都以\r\n結尾。

redis的通訊協議包括:消息頭標識和消息行,消息行里可能還有一個數據塊大小的描述。redis是以行來划分的,每行以\r\n結束,且每一行都有一個消息頭

消息體一共分為5種類型:

(1)[+]表示一個正確的狀態信息,具體信息是:當前行+后面的字符
(2)[-]表示一個錯誤信息,具體信息是:當前行-后面的字符
(3)[]表示消息體總共有多少行,不包括當前行,后面是具體的行數
(4)[ ]\r\n 后面則是對應長度的數據
(5)[:]表示返回一個數值,:后面是相應的數字字符

redis命令會返回多種不同類型的回復,通過檢查服務器發回數據的第一個字節,可以確定這個回復是什么類型:

(1)狀態回復的第一個字節是[+]
(2)錯誤回復的第一個字節是[-]
(3)整數回復的第一個字節是[:]
(4)批量回復的第一個字節是[$]
(5)多條批量回復的第一個字節是[*]

具體指令和返回:

1、SET

client : SET MYTEST HELLO

設置MYTEST的值為HELLO。
在redis的通訊協議上會以空格把命令拆分成三行,得到最終的命令如下:

*3\r\n     --消息體總共3行
$3\r\n --下一行數據的長度“SET”為3
SET\r\n
$
6\r\n --下一行數據的長度“MYTEST”為6
MYTEST\r\n
$5\r\n --下一行數據的長度“HELLO”為5
HELLO\r\n

server :
服務端操作成功:+OK\r\n
如果服務端返回錯誤: -錯誤信息\r\n

2、GET

client : GET MYTEST

產生的通訊指令:

*2\r\n
$3\r\n
GET\r\n
$
6\r\n
MYTEST

server : 如果存在這個key則返回:

$5\r\n
HELLO\r\n

3、HKEYS

 client : HKEYS MYTEST

以上命令是獲取對應MYTEST有多少個field成員:

*2\r\n
$5\r\n
HKEYS\r\n
$
6\r\n
MYTEST\r\n

server :
如果不存在任何字段信息:*0\r\n
如果存在一個AGE字段信息:

*1\r\n
$2\r\n
AGE\r\n

4、HMGET

client :
HMGET MYTEST AGE

以上命令是獲取HENRY的QQ信息。

*3\r\n
$5\r\n
HMGET\r\n
$
6\r\n
MYTEST\r\n
$3\r\n
AGE\r\n

server :

如果不存在字段值

*1\r\n
$-1\r\n

存在字段值

*1\r\n
$2\r\n
28\r\n

以上主要列舉Redis普遍處理的一些情況,由於指令太多就不一一列舉了,如果有需要自己實現Client的朋友可以到Redis官方看相關命令文檔。redis官方文檔

練習小程序:通過socket和redis server進行通信

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
public class SimpleProtocol {
public static void main(String[] args) throws Exception{
Socket socket = new Socket();
socket.setReuseAddress(true);
//空閑時發送數據包,確認服務端狀態
socket.setKeepAlive(true);
//關閉Nagle算法,盡快發送
socket.setTcpNoDelay(true);
socket.setSoLinger(true, 0);
//連接server
socket.connect(new InetSocketAddress("localhost", 6379), 3000);
//設置讀取時超時時間
socket.setSoTimeout(3000);
OutputStream os = socket.getOutputStream();
/**
* SET 命令
* 協議: array 3個元素 SET simpleKey simpleValue
*/

os.write(getBytes("*3\\r\\n$3\\r\\nSET\\r\\n$9\\r\\nsimpleKey\\r\\n$11\\r\\nsimpleValue\\r\\n"));
os.flush();
InputStream is = socket.getInputStream();
/**
* 解析SET命令的返回結果
*/

String result = analysisResult(is);
System.out.println("SET command response : " + result);
System.out.println();
/**
* GET 命令
* 協議: array 2個元素 GET simpleKey
*/

os.write(getBytes("*2\\r\\n$3\\r\\nGET\\r\\n$9\\r\\nsimpleKey\\r\\n"));
os.flush();
/**
* 解析GET命令返回結果
*/

String value = analysisResult(is);
System.out.println("GET command response : " + value);
is.close();
os.close();
socket.close();
}
/**
* 解析返回結果
* @param is
* @return
* @throws Exception
*/

private static String analysisResult(InputStream is) throws Exception{
/**
* 第一個字節指定返回的數據結構類型
*/

byte type = (byte)is.read();
System.out.println("response type is : " + (char)type);
if(type == '+'){
//Simple String類型
return readCRLF(is);
}else if(type == '$'){
//Bulk String類型
int len = readIntCRLF(is);
System.out.println("$ value len : " + len);
return readFixedLen(is, len);
}
return null;
}
/**
* 讀取int值,直到遇到CRLF
* @param is
* @return
* @throws Exception
*/

private static int readIntCRLF(InputStream is) throws Exception{
return Integer.parseInt(readCRLF(is));
}
/**
* 讀取字符串,直到遇到CRLF
* @param is
* @return
* @throws Exception
*/

private static String readCRLF(InputStream is) throws Exception{
byte b = (byte)is.read();
StringBuilder sb = new StringBuilder();
//不是最后一個輸入字節時
while(b != -1){
//判斷是否是CR,如果不是加入sb中
if(b != '\\r'){
sb.append((char)b);
}else{
//如果是CR,繼續讀取一個字節,如果不是LF,報錯
byte oneMore = (byte)is.read();
if(oneMore != '\\n'){
throw new RuntimeException("CRLF error!");
}else{
break;
}
}
b = (byte)is.read();
}
return sb.toString();
}
/**
* 讀取固定字節長度的字符串
* @param is
* @param len
* @return
* @throws Exception
*/

private static String readFixedLen(InputStream is, int len) throws Exception{
byte[] bytes = new byte[len];
for(int i = 0; i < len; i++){
bytes[i] = (byte)is.read();
}
//CR
is.read();
//LF
is.read();
return new String(bytes, "UTF-8");
}
private static byte[] getBytes(String str) throws Exception{
return str.getBytes(Charset.forName("UTF-8"));
}
}

運行結果;

 response type is : +
SET command response : OK

response type is : $
$ value len : 11
GET command response : simpleValue

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com