当前位置:网站首页>实现基于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的控制台输出颜色乱码,不支持颜色的转义。
如果文章对你有用,狠狠地三连支持一下吧!!!
边栏推荐
- 1、 Downloading and installing appium
- Jsup supports XPath
- Eureka source code shallow reading - automatic fault removal
- R for Data Science (notes) -- data transformation (used by filter)
- Clustered index (clustered index), nonclustered index (nonclustered index)
- Bytebase joins Alibaba cloud polardb open source database community
- Understanding openstack network
- UART communication (STM32F103 library function)
- 【CANN文档速递05期】一文让您了解什么是算子
- Introduction: continuously update the self-study version of the learning manual for junior test development engineers
猜你喜欢
对“宁王”边卖边买,高瓴资本“高抛低吸”已套现数十亿
Application practice | massive data, second level analysis! Flink+doris build a real-time data warehouse scheme
天天鉴宝暴雷背后:拖欠数千万、APP停摆,创始人预谋跑路?
Where are Xiaomi mobile phone's favorite SMS and how to delete them
[go language questions] go from 0 to entry 4: advanced usage of slice, elementary review and introduction to map
Win7 10 tips for installing Office2010 five solutions for installing MSXML components
Two solutions to the problem of 0xv0000225 unable to start the computer
Volcano becomes spark default batch scheduler
数字孪生行业案例:智慧港口数字化
Mq-2 smoke concentration sensor (STM32F103)
随机推荐
天天鉴宝暴雷背后:拖欠数千万、APP停摆,创始人预谋跑路?
Geoscience remote sensing data collection online
工作6年,月薪3W,1名PM的奋斗史
Confirm whether the host is a large terminal or a small terminal
IP address to integer
Apache+PHP+MySQL环境搭建超详细!!!
Redis error: -bash: redis cli: command not found
Zadig + cave Iast: let safety dissolve in continuous delivery
Jsup supports XPath
华为云ModelArts第四次蝉联中国机器学习公有云服务市场第一!
Making startup U disk -- Chinese cabbage U disk startup disk making tool V5.1
JVM tuning
Instruction rearrangement concept
用手机摄像头就能捕捉指纹?!准确度堪比签字画押,专家:你们在加剧歧视
redis数据结构之压缩列表
Information theory of popular science Shannon
three. Basic framework created by JS
Docker installing Oracle
R for Data Science (note) -- data transformation (select basic use)
Kubernetes集群部署