自己动手写Redis客户端(C#实现)3 - GET请求和批量回复
实现代码(C#)
1、发送GET指令
string keyGet = "SetKeyTest"; // 设置 的key
StringBuilder sbSendGet = new StringBuilder();
sbSendGet.Append("*2\r\n"); // 参数数量 3
string cmdGet = "GET";
sbSendGet.Append("$" + Encoding.UTF8.GetBytes(cmdGet).Length + "\r\n"); // 参数1的长度
sbSendGet.Append(cmdGet + "\r\n");// 参数1( GET指令 ) sbSendGet.Append("$" + Encoding.UTF8.GetBytes(keyGet).Length + "\r\n"); // 参数2的长度
sbSendGet.Append("" + keyGet + "\r\n");// 参数2(GET 的 KEY)
Console.WriteLine("发送的命令:");
Console.Write(sbSendGet.ToString());
byte[] dataGet = Encoding.UTF8.GetBytes(sbSendGet.ToString()); // 把请求转换为byte数组
s.Send(dataGet); // 发送指令
2、接收批量回复
byte[] resultGET = new byte[];
int resultGetLength = s.Receive(resultGET); // 接收回复 // 根据接收到的数据长度重新组装一个结果
byte[] newResultGet = new byte[resultGetLength];
for (int i = ; i < resultGetLength; i++)
{
newResultGet[i] = resultGET[i];
}
string strGetResult = Encoding.UTF8.GetString(newResultGet); // 把结果转换为string
Console.Write("获取的值:"+strGetResult);
3、 结果:

代码重构
1、发送指令
/// <summary>
/// 发送指令
/// </summary>
/// <param name="client"></param>
/// <param name="datas"></param>
/// <returns></returns>
public static string SendCmd(this Socket client, params byte[][] datas) {
client.Send(Encoding.UTF8.GetBytes("*" + datas.Length + "\r\n"));
for (int i = ; i < datas.Length; i++)
{
client.Send(Encoding.UTF8.GetBytes("$" + datas[i].Length));
client.Send(Encoding.UTF8.GetBytes("\r\n"));
client.Send(datas[i]);
client.Send(Encoding.UTF8.GetBytes("\r\n"));
}
return Reply(client);
}
2、接收回复
/// <summary>
/// 接收回复
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
public static string Reply(Socket client) {
BufferedStream s = new BufferedStream(new NetworkStream(client));
int b = s.ReadByte(); // 读取第一个字节
string result;
switch (b)
{
// 状态回复(status reply)的第一个字节是 "+"
case '+':
result = ReadLine(s);
return "+"+result;
// 错误回复(error reply)的第一个字节是 "-"
case '-':
result = ReadLine(s);
throw new Exception(result); // 抛出异常
// 整数回复(integer reply)的第一个字节是 ":"
case ':':
result = ReadLine(s);
return ":" + result;
// 批量回复(bulk reply)的第一个字节是 "$"
case '$':
result = ReadLine(s); // 先读取数据字节数
Console.WriteLine("$"+result);
int count = int.Parse(result);
// 如果被请求的值不存在, 那么批量回复会将特殊值 -1 用作回复的长度值,
if (count == -)
{
return null;
}
result = ReadByLength(s, count);
Console.WriteLine(result);
return result;
// 多条批量回复(multi bulk reply)的第一个字节是 "*"
case '*':
result = ReadLine(s); // 先读取数据行数
Console.WriteLine("*" + result);
int rows = int.Parse(result);
StringBuilder sb = new StringBuilder();
for (int i = ; i < rows; i++)
{
result = ReadLine(s);
sb.AppendLine(result);
result = ReadLine(s);
sb.AppendLine(result);
}
Console.WriteLine(sb); return sb.ToString();
default:
break;
}
return "";
}
/// <summary>
/// 按长度读取
/// </summary>
/// <param name="s"></param>
/// <param name="l"></param>
/// <returns></returns>
public static string ReadByLength(BufferedStream s, long l) {
byte[] bytes = new byte[l];
var r= s.Read(bytes,,(int)l);
return Encoding.UTF8.GetString(bytes);
}
/// <summary>
/// 按行读取
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string ReadLine(BufferedStream s)
{
StringBuilder sb = new StringBuilder();
int b = ;
while ((b = s.ReadByte()) != -)
{
if (b == '\r')
{
if ((b = s.ReadByte()) != -)
{
if (b == '\n')
{
break;
}
else
{
sb.Append('\r');
}
}
else
{
break;
} }
sb.Append((char)b);
}
return sb.ToString();
}
3、GET和SET指令
public static bool Set(this Socket client, string key, string value) {
return Set(client, key, Encoding.UTF8.GetBytes(value));
}
public static bool Set(this Socket client, string key, byte[] value)
{
string result = SendCmd(client, Encoding.UTF8.GetBytes("SET"), Encoding.UTF8.GetBytes(key), value);
Console.WriteLine(result);
return result == "+OK"; // 如果+OK 则表示设置成功!
//string
}
public static string Get(this Socket client, string key)
{
return SendCmd(client, Encoding.UTF8.GetBytes("GET"), Encoding.UTF8.GetBytes(key));
//string
}
4、重构后的代码
#region SET
string key = "SetKeyTest"; // 设置 的key
string value = "设置的值"; // 设置的值
var result = s.Set(key, value);
Console.WriteLine(result ? "设置成功!" : "设置失败!"); // 判断设置是否成功
#endregion #region 发送指令Get
string keyGet = "SetKeyTest"; // 设置 的key var resultGet = s.Get(keyGet); // 发送指令
Console.Write("获取的值:" + resultGet);
#endregion
是不是简洁很多???
5、结果

自己动手写Redis客户端(C#实现)3 - GET请求和批量回复的更多相关文章
- 自己动手写Redis客户端- Redis协议(1)
网络层 客户端和服务器通过 TCP 连接来进行数据交互, 服务器默认的端口号为 6379 . 客户端和服务器发送的命令或数据一律以 \r\n (CRLF)结尾. 请求 Redis 服务器接受命令以及命 ...
- 自己动手写Redis客户端(C#实现)2 - SET请求和状态回复(set)
Redis请求协议的一般形式: *<参数数量> CR LF $<参数 的字节数量> CR LF <参数 的数据> CR LF ... $<参数 N 的字节数量 ...
- 自己动手写Redis客户端(C#实现)4 - 整数回复
整数回复 整数回复就是一个以 ":" 开头, CRLF 结尾的字符串表示的整数. 比如说, ":0\r\n" 和 ":1000\r\n" 都 ...
- 从零开始写redis客户端(deerlet-redis-client)之路——第一个纠结很久的问题,restore引发的血案
引言 正如之前的一篇博文,LZ最近正在从零开始写一个redis的客户端,主要目的是为了更加深入的了解redis,当然了,LZ也希望deerlet客户端有一天能有一席之地.在写的过程当中,LZ遇到了一个 ...
- 用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群
想没想过,自己写一个redis客户端,是不是很难呢? 其实,并不是特别难. 首先,要知道redis服务端用的通信协议,建议直接去官网看,博客啥的其实也是从官网摘抄的,或者从其他博客抄的(忽略). 协议 ...
- 手写redis客户端
一.RESP通信协议 Redis Serialization Protocol (Redis序列化协议). 特点:容易实现.解析快.可读性强 以\r\n分割数据. 二.撸代码 package com. ...
- 用BIO手写实现Redis客户端的探究(拒绝Jedis)
在Redis的使用过程中,大多数人都是使用现成的客户端,如Jedis,Redisson,Lettuce.因此本文研究用BIO的方式手写Redis客户端尝试,对遇到的问题进行探究及总结. Redis通讯 ...
- 解决ASP.NET中Redis 每小时6000次访问请求的问题
原文:解决ASP.NET中Redis 每小时6000次访问请求的问题 虽然ServiceStack v4是商业支持的产品,但我们也允许免费使用小型项目和评估目的.上面的NuGet包中包含可以使用许可证 ...
- 学习T-io框架,从写一个Redis客户端开始
前言 了解T-io框架有些日子了,并且还将它应用于实战,例如 tio-websocket-server,tio-http-server等.但是由于上述两个server已经封装好,直接应用就可以.所 ...
随机推荐
- 3-ftp搭建成功,服务器能访问,外网无法连接和访问
登录 ECS 管理控制台,找到相应的实例. 在实例的右侧单击管理,进入实例详情页面.选择本实例安全组. 在安全组列表页面,找到相应的安全组,单击配置规则. 在安全组规则页面,单击添加安全组规则. 在添 ...
- 简单的makefile
单一程序 准备一个hello.c #include <stdio.h> int main(void) { printf("Hello World!\n"); } gcc ...
- Android activity创建三部曲
1.新建类继承Activity或其子类,现在一般继承AppCompatActivity public class TestActivity extends AppCompatActivity { @O ...
- 第三周java学习总结
学号 20175206 <Java程序设计>第三周学习总结 教材学习内容总结 本周为第四章的学习,分为以下几个方面: 1.包与代码组织 2.String类 3.对象创建 4.包装类 经过代 ...
- Springboot集成Spring Batch
Spring官网 (https://spring.io/projects/spring-batch#overview)对Spring Batch的解释: 一个轻量级的.全面的批处理框架,用于开发对企 ...
- react native 中时间选择插件
npm install react-native-datepicker --save import DatePicker from 'react-native-datepicker'; <Vie ...
- bzoj 4244 括号序列dp
将各种情况绕环等看作括号序列,括号内的区域上下都需要累加答案,左右也是 f[i][j] 代表 前i个车站已经处理完的有j个左括号的最小权值 我们可以发现,更新的来源来自于 i-1, 和 i 将上 描述 ...
- json 格式化的时候,日期格式化
public class CustomIsoDateTimeConverter : IsoDateTimeConverter { public CustomIsoDateTimeConverter() ...
- 组件或者dom的特殊属性
key:用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes. 常用姿势: 1.结合 v-for,有相同父元素的子元素必须有唯一key. <ul> <li ...
- Doom HDU - 5239 (找规律+线段树)
题目链接: D - Doom HDU - 5239 题目大意:首先是T组测试样例,然后n个数,m次询问,然后每一次询问给你一个区间,问你这个这段区间的加上上一次的和是多少,查询完之后,这段区间里 ...