【Redis学习】redis通讯协议
服務(wù)是一個(gè)較抽象的概念,意在幫助使用者達(dá)到某種需求。那么對于網(wǎng)絡(luò)服務(wù)來說,通常我們可以將其定義為一個(gè)運(yùn)行在操作系統(tǒng)上的程序,使用者通過網(wǎng)絡(luò)與其進(jìn)行交互并得到想要的信息。
客戶端和服務(wù)器通過TCP連接來進(jìn)行數(shù)據(jù)交互,所以它和其它網(wǎng)絡(luò)服務(wù)一樣有一個(gè)協(xié)議。redis服務(wù)器接受命令以及命令的參數(shù)。服務(wù)器會(huì)在接到命令之后對命令進(jìn)行處理,并將命令的回復(fù)傳送到客戶端。具體在網(wǎng)絡(luò)層上就是:Redis在TCP端口6379上監(jiān)聽到來的連接,客戶端連接到來時(shí),Redis服務(wù)器為此創(chuàng)建一個(gè)TCP連接。在客戶端與服務(wù)器端之間傳輸?shù)拿總€(gè)Redis命令或者數(shù)據(jù)都以\r\n結(jié)尾。
redis的通訊協(xié)議包括:消息頭標(biāo)識和消息行,消息行里可能還有一個(gè)數(shù)據(jù)塊大小的描述。redis是以行來劃分的,每行以\r\n結(jié)束,且每一行都有一個(gè)消息頭。
消息體一共分為5種類型:
(1)[+]表示一個(gè)正確的狀態(tài)信息,具體信息是:當(dāng)前行+后面的字符
(2)[-]表示一個(gè)錯(cuò)誤信息,具體信息是:當(dāng)前行-后面的字符
(3)[]表示消息體總共有多少行,不包括當(dāng)前行,后面是具體的行數(shù)
(4)[]表示下一行數(shù)據(jù)長度,不包括換行符\r\n的長度,]表示下一行數(shù)據(jù)長度,不包括換行符\r\n的長度,后面則是對應(yīng)長度的數(shù)據(jù)
(5)[:]表示返回一個(gè)數(shù)值,:后面是相應(yīng)的數(shù)字字符
redis命令會(huì)返回多種不同類型的回復(fù),通過檢查服務(wù)器發(fā)回?cái)?shù)據(jù)的第一個(gè)字節(jié),可以確定這個(gè)回復(fù)是什么類型:
(1)狀態(tài)回復(fù)的第一個(gè)字節(jié)是[+]
(2)錯(cuò)誤回復(fù)的第一個(gè)字節(jié)是[-]
(3)整數(shù)回復(fù)的第一個(gè)字節(jié)是[:]
(4)批量回復(fù)的第一個(gè)字節(jié)是[$]
(5)多條批量回復(fù)的第一個(gè)字節(jié)是[*]
具體指令和返回:
1、SET
client : SET MYTEST HELLO設(shè)置MYTEST的值為HELLO。
在redis的通訊協(xié)議上會(huì)以空格把命令拆分成三行,得到最終的命令如下:
server :
服務(wù)端操作成功:+OK\r\n
如果服務(wù)端返回錯(cuò)誤: -錯(cuò)誤信息\r\n
2、GET
client : GET MYTEST產(chǎn)生的通訊指令:
*2\r\n $3\r\n GET\r\n $6\r\n MYTESTserver : 如果存在這個(gè)key則返回:
$5\r\n HELLO\r\n3、HKEYS
client : HKEYS MYTEST以上命令是獲取對應(yīng)MYTEST有多少個(gè)field成員:
*2\r\n $5\r\n HKEYS\r\n $6\r\n MYTEST\r\nserver :
如果不存在任何字段信息:*0\r\n
如果存在一個(gè)AGE字段信息:
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\nserver :
如果不存在字段值
*1\r\n $-1\r\n存在字段值
*1\r\n $2\r\n 28\r\n以上主要列舉Redis普遍處理的一些情況,由于指令太多就不一一列舉了,如果有需要自己實(shí)現(xiàn)Client的朋友可以到Redis官方看相關(guān)命令文檔。redis官方文檔
練習(xí)小程序:通過socket和redis server進(jìn)行通信
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);//空閑時(shí)發(fā)送數(shù)據(jù)包,確認(rèn)服務(wù)端狀態(tài)socket.setKeepAlive(true);//關(guān)閉Nagle算法,盡快發(fā)送socket.setTcpNoDelay(true);socket.setSoLinger(true, 0);//連接serversocket.connect(new InetSocketAddress("localhost", 6379), 3000);//設(shè)置讀取時(shí)超時(shí)時(shí)間socket.setSoTimeout(3000);OutputStream os = socket.getOutputStream();/*** SET 命令* 協(xié)議: array 3個(gè)元素 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命令的返回結(jié)果*/String result = analysisResult(is);System.out.println("SET command response : " + result);System.out.println();/*** GET 命令* 協(xié)議: array 2個(gè)元素 GET simpleKey*/os.write(getBytes("*2\\r\\n$3\\r\\nGET\\r\\n$9\\r\\nsimpleKey\\r\\n"));os.flush();/*** 解析GET命令返回結(jié)果*/String value = analysisResult(is);System.out.println("GET command response : " + value);is.close();os.close();socket.close();}/*** 解析返回結(jié)果* @param is* @return* @throws Exception*/private static String analysisResult(InputStream is) throws Exception{/*** 第一個(gè)字節(jié)指定返回的數(shù)據(jù)結(jié)構(gòu)類型*/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();//不是最后一個(gè)輸入字節(jié)時(shí)while(b != -1){//判斷是否是CR,如果不是加入sb中if(b != '\\r'){sb.append((char)b);}else{//如果是CR,繼續(xù)讀取一個(gè)字節(jié),如果不是LF,報(bào)錯(cuò)byte oneMore = (byte)is.read();if(oneMore != '\\n'){throw new RuntimeException("CRLF error!");}else{break;}}b = (byte)is.read();}return sb.toString();}/*** 讀取固定字節(jié)長度的字符串* @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();}//CRis.read();//LFis.read();return new String(bytes, "UTF-8");}private static byte[] getBytes(String str) throws Exception{return str.getBytes(Charset.forName("UTF-8"));} }運(yùn)行結(jié)果;
response type is : +SET command response : OKresponse type is : $$ value len : 11GET command response : simpleValue 超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的【Redis学习】redis通讯协议的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: web性能压测——webbench
- 下一篇: 【Redis学习】Redis开启多个端口