实现代码(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请求和批量回复的更多相关文章

  1. 自己动手写Redis客户端- Redis协议(1)

    网络层 客户端和服务器通过 TCP 连接来进行数据交互, 服务器默认的端口号为 6379 . 客户端和服务器发送的命令或数据一律以 \r\n (CRLF)结尾. 请求 Redis 服务器接受命令以及命 ...

  2. 自己动手写Redis客户端(C#实现)2 - SET请求和状态回复(set)

    Redis请求协议的一般形式: *<参数数量> CR LF $<参数 的字节数量> CR LF <参数 的数据> CR LF ... $<参数 N 的字节数量 ...

  3. 自己动手写Redis客户端(C#实现)4 - 整数回复

    整数回复 整数回复就是一个以 ":" 开头, CRLF 结尾的字符串表示的整数. 比如说, ":0\r\n" 和 ":1000\r\n" 都 ...

  4. 从零开始写redis客户端(deerlet-redis-client)之路——第一个纠结很久的问题,restore引发的血案

    引言 正如之前的一篇博文,LZ最近正在从零开始写一个redis的客户端,主要目的是为了更加深入的了解redis,当然了,LZ也希望deerlet客户端有一天能有一席之地.在写的过程当中,LZ遇到了一个 ...

  5. 用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群

    想没想过,自己写一个redis客户端,是不是很难呢? 其实,并不是特别难. 首先,要知道redis服务端用的通信协议,建议直接去官网看,博客啥的其实也是从官网摘抄的,或者从其他博客抄的(忽略). 协议 ...

  6. 手写redis客户端

    一.RESP通信协议 Redis Serialization Protocol (Redis序列化协议). 特点:容易实现.解析快.可读性强 以\r\n分割数据. 二.撸代码 package com. ...

  7. 用BIO手写实现Redis客户端的探究(拒绝Jedis)

    在Redis的使用过程中,大多数人都是使用现成的客户端,如Jedis,Redisson,Lettuce.因此本文研究用BIO的方式手写Redis客户端尝试,对遇到的问题进行探究及总结. Redis通讯 ...

  8. 解决ASP.NET中Redis 每小时6000次访问请求的问题

    原文:解决ASP.NET中Redis 每小时6000次访问请求的问题 虽然ServiceStack v4是商业支持的产品,但我们也允许免费使用小型项目和评估目的.上面的NuGet包中包含可以使用许可证 ...

  9. 学习T-io框架,从写一个Redis客户端开始

    前言   了解T-io框架有些日子了,并且还将它应用于实战,例如 tio-websocket-server,tio-http-server等.但是由于上述两个server已经封装好,直接应用就可以.所 ...

随机推荐

  1. 第四十六篇--解析和保存xml文件

    新建assets资源文件夹,右键app --> new --> Folder --> Assets Folder,将info.xml放入此文件夹下面. info.xml <?x ...

  2. C# webapi 上传下载图片

    客户端上传文件 string url = url + "webUploadFile"; Uri server = new Uri(url); HttpClient httpClie ...

  3. Apicloud学习第一天

    Apicloud学习    apiready = function() {}  //dom加载完后执行 api.openWin({ //打开新的window name: 'main', //windo ...

  4. python小练习: 给定一个数组 按重复次数 降序排列输出 数组非空且为正整数

    假设有个列表  a=[1,1,1,2,2,4,5,5,5,5] (非空且为正整数) 那么根据要求 最终输出的形式为  5,1,2,4  (按重复次数 降序排列输出) 代码实现及解释: a=[1,1,1 ...

  5. 使用numpy的小惊喜

    今天使用 numpy.true_divide 发现个有趣的事情, 下面的代码18.19行如果去掉,就会报下面的  RuntimeWarning def multivalue_divide(timese ...

  6. macOS Mojave配置OpenGL开发环境

    ---恢复内容开始--- 前言: 本文写作目的: 是由于本人参考 csdn原文 的方法配置环境时踩了很多坑,所以在此写一篇文防止以后用到. 工具: Xcode CMake 步骤: 准备工作: Xcod ...

  7. 【OpenGL】代码记录01创建窗口

    创建空窗口: #include<iostream> // GLEW #define GLEW_STATIC #include <GL/glew.h> // GLFW #incl ...

  8. 20155324《网络对抗》Exp1 PC平台逆向破解(5)M

    20155324<网络对抗>Exp1 PC平台逆向破解(5)M 实验目标 本次实践的对象是一个名为~pwn1~的~linux~可执行文件. 该程序正常执行流程是:~main~调用~foo~ ...

  9. Thunar 通过快捷键在当前文件夹打开终端

    参考『Keyboard shortcut for open terminal here in thunar』\(^{[1]}\) 在 ganiserb/thunar-terminal 下载脚本. 将脚 ...

  10. T-SQL实用查询之查询字段所属的数据库表

    SELECT b.name as TableName,a.name as columnname From syscolumns a INNER JOIN sysobjects b ON a.id=b. ...