自己动手写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[512]; int resultGetLength = s.Receive(resultGET); // 接收回复 // 根据接收到的数据长度重新组装一个结果 byte[] newResultGet = new byte[resultGetLength]; for (int i = 0; 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 = 0; 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 == -1) { 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 = 0; 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,0,(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 = 0; while ((b = s.ReadByte()) != -1) { if (b == '\r') { if ((b = s.ReadByte()) != -1) { 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、结果