当前位置:网站首页>实现基于Socket自定义的redis简单客户端
实现基于Socket自定义的redis简单客户端
2022-06-24 19:02:00 【枫蜜柚子茶】
一、RESP协议
首先需要明白,Redis是一个CS架构的软件,通信一般分两步(不包括pipeline和PubSub):
- 客户端(client)向服务端(server)发送一条命令
- 服务端解析并执行命令,返回响应结果给客户端 因此客户端发送命令的格式、服务端响应结果的格式必须有一个规范,这个规范就是通信协议。
而在Redis中采用的是RESP(Redis Serialization Protocol)协议:
- Redis 1.2版本引入了RESP协议
- Redis 2.0版本中成为与Redis服务端通信的标准,称为
- RESP2 Redis 6.0版本中,从RESP2升级到了RESP3协议,增加了更多数据类型并且支持6.0的新特性--客户端缓存
在RESP中,通过首字节的字符来区分不同数据类型,常用的数据类型包括5种:
- 单行字符串:首字节是 ‘+’ ,后面跟上单行字符串,以CRLF( "\r\n" )结尾。例如返回"OK": "+OK\r\n"
- 错误(Errors):首字节是 ‘-’ ,与单行字符串格式一样,只是字符串是异常信息,例如:"-Error message\r\n"
- 数值:首字节是 ‘:’ ,后面跟上数字格式的字符串,以CRLF结尾。例如:":10\r\n"
- 多行字符串:首字节是 ‘$’ ,表示二进制安全的字符串,最大支持512MB:如果大小为0,则代表空字符串:"$0\r\n\r\n" 如果大小为-1,则代表不存在:"$-1\r\n"

- 数组:首字节是 ‘*’,后面跟上数组元素个数,再跟上元素,元素数据类型不限:例如

二、模拟redis客户端实现
【响应解析请求模块】
/**
* 解析响应请求信息
*
* @return 解析结果
*/
private static Object handleResponse() throws IOException {
//五种情况读取数据
int opt = READER.read();
switch (opt) {
case '+'://单行字符串,读取单行信息
return READER.readLine();
case '-'://异常信息,读取单行信息返回异常
return READER.readLine();
case ':'://数值类型,读取单行
return Long.parseLong(READER.readLine());
case '*':
return readBulkString();
case '$'://读取多行字符串
int len = Integer.parseInt(READER.readLine());
if (len == -1) {
return null;
} else if (len == 0) {
return "";
} else {
return READER.readLine();
}
default:
throw new RuntimeException("错误的数据格式!");
}
}
/**
* 数组结果解析
*
* @return
* @throws IOException
*/
private static Object readBulkString() throws IOException {
//获取数组大小
int size = Integer.parseInt(READER.readLine());
if (size <= 0) {
return null;
} else {
List<Object> result = new ArrayList<>();
for (int i = 0; i < size; i++) {
result.add(handleResponse());
}
return result;
}
}【完整代码】
/**
* @Author 蜂蜜柚子茶
* @Date 2022/6/14 20:34
*/
public class MyRedisClient {
private static Socket socket;
private static PrintWriter WRITER;
private static BufferedReader READER;
private static BufferedReader KEYBOARD_INPUT;
private static final String INFO = "127.0.0.1:6379> ";
public static void main(String[] args) throws Exception {
try {
//建立连接
//虚拟机IP地址:192.168.29.128
socket = new Socket("192.168.29.128", 6379);
//获取输入流、输出流
WRITER = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
READER = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
//键盘输入命令
KEYBOARD_INPUT = new BufferedReader(new InputStreamReader(System.in));
//执行命令,同时结果解析
execute();
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放连接
try {
if (READER != null)
READER.close();
if (WRITER != null)
WRITER.close();
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取键盘输入
*
* @return
* @throws Exception
*/
public static String getInput() throws Exception { // 键盘信息输入
System.out.print(INFO);
return KEYBOARD_INPUT.readLine();
}
/**
* 执行命令
*
* @throws IOException
*/
private static void execute() throws Exception {
while (true) {
//获取输入命令,去除首位空格
String string = getInput().trim();
//解析命令,去除所有空格
String replace = string.replaceAll("\\s{1,}", "/");
//System.out.println(replace);
String[] strings = replace.split("/");
//发送请求
sendRequest(strings);
//解析响应信息
Object result = handleResponse();
if (result == null) {
System.out.println(getFormatResult("null", "warning"));
} else if (result.toString().startsWith("ERR")) {
System.out.println(getFormatResult(result.toString(), "error"));
} else {
System.out.println(getFormatResult(result.toString(), "info"));
}
}
}
/**
* 格式化输出结果
*
* @param content 结果
* @param type 类型
* @return 格式化输出结果
*/
private static String getFormatResult(String content, String type) {
if (type.equals("error")) {
return String.format("\033[%dm%s\033[0m", 31, content);
} else if (type.equals("info")) {
return String.format("\033[%dm%s\033[0m", 34, content);
} else if (type.equals("warning")) {
return String.format("\033[%dm%s\033[0m", 33, content);
} else {
return content;
}
}
/**
* 解析响应请求信息
*
* @return 解析结果
*/
private static Object handleResponse() throws IOException {
//五种情况读取数据
int prefix = READER.read();
switch (prefix) {
case '+'://单行字符串,读取单行信息
return READER.readLine();
case '-'://异常信息,读取单行信息返回异常
return READER.readLine();
case ':'://数值类型,读取单行
return Long.parseLong(READER.readLine());
case '*':
return readBulkString();
case '$'://读取多行字符串
int len = Integer.parseInt(READER.readLine());
if (len == -1) {
return null;
} else if (len == 0) {
return "";
} else {
return READER.readLine();
}
default:
throw new RuntimeException("错误的数据格式!");
}
}
/**
* 数组结果解析
*
* @return
* @throws IOException
*/
private static Object readBulkString() throws IOException {
//获取数组大小
int size = Integer.parseInt(READER.readLine());
if (size <= 0) {
return null;
} else {
List<Object> result = new ArrayList<>();
for (int i = 0; i < size; i++) {
result.add(handleResponse());
}
return result;
}
}
/**
* 发送请求信息
*
* @param args
*/
private static void sendRequest(String... args) {
//本质上是命令--> set name XXXX
WRITER.println("*" + args.length);
for (String arg : args) {
WRITER.println("$" + arg.getBytes(StandardCharsets.UTF_8).length);
WRITER.println(arg);
}
//清空缓冲区
WRITER.flush();
}
}三、效果展示
【模拟redis-cli】idear窗口
win的控制台输出颜色乱码,不支持颜色的转义。

如果文章对你有用,狠狠地三连支持一下吧!!!
边栏推荐
- Programmers spend most of their time not writing code, but...
- The name of the button in the Siyuan notes toolbar has changed to undefined. Has anyone ever encountered it?
- Get to know the data structure of redis - hash
- Teach you how to cancel computer hibernation
- Drawing DEM with GEE gracefully
- 全链路业务追踪落地实践方案
- 【CANN文档速递05期】一文让您了解什么是算子
- 【云驻共创】ModelBox隔空作画 绘制你的专属画作
- 【CANN文档速递06期】初识TBE DSL算子开发
- php OSS文件读取和写入文件,workerman生成临时文件并输出浏览器下载
猜你喜欢

Methods for comparing float types in the kernel

Geoscience remote sensing data collection online

工作6年,月薪3W,1名PM的奋斗史

对国产数据库厂商提几个关于SQL引擎的小需求

What are the functions of IBPs open source form designer?

Based on STM32F103 0.96 inch OLED LCD driver (IIC communication)

Two solutions to the problem of 0xv0000225 unable to start the computer
![[video tutorial] functions that need to be turned off in win10 system. How to turn off the privacy option in win10 computer](/img/14/0313857adc178ecee4c866a05e54aa.jpg)
[video tutorial] functions that need to be turned off in win10 system. How to turn off the privacy option in win10 computer

Making startup U disk -- Chinese cabbage U disk startup disk making tool V5.1

Vs2017 setting function Chinese Notes
随机推荐
Pingcap was selected as the "voice of customers" of Gartner cloud database in 2022, and won the highest score of "outstanding performer"
An accident caused by a MySQL misoperation cannot be withstood by High Availability!
Volcano成Spark默认batch调度器
Accurate calculation of task progress bar of lol mobile game
天天鉴宝暴雷背后:拖欠数千万、APP停摆,创始人预谋跑路?
Digital twin industry case: Digital Smart port
[video tutorial] functions that need to be turned off in win10 system. How to turn off the privacy option in win10 computer
Volcano becomes spark default batch scheduler
Error in Android connection database query statement
Technology implementation | Apache Doris cold and hot data storage (I)
Server lease error in Hong Kong may lead to serious consequences
敏捷之道 | 敏捷开发真的过时了么?
Comparative analysis of arrayblockingqueue and linkedblockingqueue
gateway
[cann document express issue 05] let you know what operators are
【Go语言刷题篇】Go从0到入门4:切片的高级用法、初级复习与Map入门学习
Get to know the data structure of redis - hash
Hutool reads large excel (over 10m) files
[cann document express issue 04] unveiling the development of shengteng cann operator
Some small requirements for SQL Engine for domestic database manufacturers