上图:Demo 跑出来的结果如图
蓝色的是cmd 的命令,黑色的是C#写的控制台程序。红色箭头部分是 redis 命令
0. Netty是Java生态圈的一个重要组件。个人认为JAVA繁荣于.NET 生态的很重要一个原因,就是缺少了这样一个NIO框架。JAVA的游戏开发,少不得使用Netty.
原生Socker编程,犹如一辆绿皮火车。Netty无疑是高铁般的存在。
1.学习DotNetty很久。从DotNetty 0.4版本。到现在的0.48版本。自己实现一个C/S端的例子。还没有太好的想法去实现。
今天看到haifeiWu 的高作《Netty 源码中对 Redis 协议的实现》,遂想跟着实现一个。
所以,才有了今天的Demo.
是的,它还只是一个Demo.并不能取代StackExchange.Redis。
2 RESP 是 Redis 序列化协议的简写。它是一种直观的文本协议,优势在于实现非常简单,解析性能极好。
Redis 协议将传输的结构数据分为 5 种最小单元类型,单元结束时统一加上回车换行符号\r\n,来表示该单元的结束。
单行字符串 以 + 符号开头。
多行字符串 以 $ 符号开头,后跟字符串长度。
整数值 以 : 符号开头,后跟整数的字符串形式。
错误消息 以 - 符号开头。
数组 以 * 号开头,后跟数组的长度。
关于 RESP 协议的具体介绍感兴趣的小伙伴请移步 haifeiWu 的另一篇文章Redis协议规范(译文)
以上第二点是摘抄自 haifeiWu中的介绍
Demo 开始
1,定义枚举 RedisMessageType
1 internal enum RedisMessageType:byte 2 { 3 /// <summary> 4 /// 以 + 开头的单行字符串 5 /// </summary> 6 SimpleString = 43, 7 8 /// <summary> 9 /// 以 - 开头的错误信息 10 /// </summary> 11 Error = 45, 12 /// <summary> 13 /// 以 : 开头的整型数据INTEGER 14 /// </summary> 15 Integer = 58, 16 /// <summary> 17 /// 以 $ 开头的多行字符串 18 /// </summary> 19 BulkString = 36, 20 21 /// <summary> 22 /// 以 * 开头的数组 23 /// </summary> 24 ArrayHeader = 42 25 }
2,定义RedisObject 并定义了虚拟的方法 WriteBuffer
1 public class RedisObject 2 { 3 public virtual void WriteBuffer(IByteBuffer output) 4 { 5 } 6 } 7 8 public class RedisCommon : RedisObject 9 { 10 public RedisCommon() 11 { 12 Commond = new List<string>(); 13 } 14 public List<string> Commond { get; set; } 15 public override void WriteBuffer(IByteBuffer output) 16 { 17 //请求头部格式, *<number of arguments>\r\n 18 //const string headstr = "*{0}\r\n"; 19 //参数信息 $<number of bytes of argument N>\r\n<argument data>\r\n 20 //const string bulkstr = "${0}\r\n{1}\r\n"; 21 StringBuilder stringBuilder = new StringBuilder(); 22 stringBuilder.AppendFormat("*{0}\r\n",Commond.Count); 23 foreach (var item in Commond) 24 { 25 stringBuilder.AppendFormat("${0}\r\n{1}\r\n",item.Length,item); 26 } 27 //*1\r\n$4\r\nPING\r\n 28 byte[] bytes = Encoding.UTF8.GetBytes(stringBuilder.ToString()); 29 output.WriteBytes(bytes); 30 } 31 }
3,定义RedisEncoder 编码器, 它集成了MessageToByteEncoder<T>方法。主要是将RedisObject,写到IByteBuffer里面。
public class RedisEncoder:DotNetty.Codecs.MessageToByteEncoder<RedisObject> { protected override void Encode(IChannelHandlerContext context, RedisObject message, IByteBuffer output) { message.WriteBuffer(output); //context.WriteAndFlushAsync(output); } }
4,定义 RedisDecoder 解码器,它继承了 ByteToMessageDecoder。
ByteToMessageDecoder 是需要自己实现解决粘包,拆包的。比较低级别,但是灵活。
DotNetty 还有其他比较高级的解码器。
比如 MessageToMessageDecoder, DatagramPacketDecoder,LengthFieldBasedFrameDecoder,LineBasedFrameDecoder,ReplayingDecoder,DelimiterBasedFrameDecoder,StringDecoder。
在蒋老师的《Netty权威指南》一书中,都能学习到。
6.定义 RedisHandle Handler ,他继承了SimpleChannelInboundHandler 的方法。用来接收解码器之后解出来的RedisObJect对象。
public class RedisHandle : SimpleChannelInboundHandler<RedisObject> { protected override void ChannelRead0(IChannelHandlerContext ctx, RedisObject msg) { if (msg is ReidsString) { ReidsString reidsString = (ReidsString)msg; Console.WriteLine(reidsString.Content); } } }