使用 DotNetty 实现 Redis 的一个控制台应用程序

零:Demo 跑出来的结果如图

上图说明

图中左边蓝色的命令行界面,是用windows powershell 命令行链接的。

  1.打开powershell命令行界面,输入命令【telnet   127.0.0.1    6379】。

   如果没有powershell,使用cmd 命令行界面也是可以达到测试redis 命令的效果的。

   输入PING 命令,redis 接收到,它将返回一个PONG字符串。命令的作用通常是测试与服务器的连接是否仍然生效。PING命令

   输入Info 命令,redis 会返回一大串的redis 服务端的信息。这个命令,主要用来测试拆包的情况,下面会讲到拆包如何处理。

图中右边黑色的命令行界面,是Demo 跑出来的控制台应用程序。

两个结果一对比,测试出来,我们的Demo已经得到了正确的结果。

Ok,下面开始进入正戏。

一 DotNetty 是什么

DotNetty 是netty 一个C#版本。

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。【摘自百度百科

  笔者认为 Netty是Java生态圈的一个重要组件。

  原生Socket编程,学习成本高,使用原生的Socket做项目,那就是开着一辆绿皮火车,动次打次。。。。

  使用Netty,开做项目,那开发效率无疑是高铁般的存在。

  而且使用原生的socket 编程是很困难的

二,写这个Demo 的起因

学习DotNetty很久。从DotNetty 0.4版本。到现在的0.48版本。自己实现一个C/S端的例子。还没有太好的想法去实现。

    今天看到haifeiWu的高作《Netty 源码中对 Redis 协议的实现》,遂想跟着实现一个。

    所以,才有了今天的Demo.

    是的,它还只是一个Demo.并不能取代StackExchange.Redis。

三,了解一下redis的协议

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权威指南》一书中,都能学习到。

  通过测试,我们知道了info 命令返回的是一个多行字符串

    以 $ 符号开头,后跟字符串长度。假设redis 服务端要返回一个多行字符串,它的返回格式为:  ${字符串长度}\r\n{字符串}\r\n

    解析多行字符串的代码为

  

        private string ReadMultiLine(IByteBuffer input)
        {
            Int64 strLength = ReadInteger(input);
            Int64 packLength = input.ReaderIndex + strLength + 2;
            //包的长度,比实际包还要大,跳过他,防止堆积
            if ( input.WriterIndex> packLength)
            {
                input.SkipBytes(input.ReadableBytes);
            }
            if (strLength == -1)
            {
                return null;
            }
            //包的长度,比实际包还小 拆包
            if (packLength > input.WriterIndex)
            {
                throw new Exception("");
            }
            int count = 0;
            int whildCount = 0;
            StringBuilder stringBuilder = new StringBuilder();
            while (input.IsReadable())
            {
                string str= this.ReadString(input);
                count += str.Length;
                stringBuilder.AppendLine(str);
                whildCount++;
            }

       return stringBuilder.ToString();
        }

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);
            }
        }
    }

结语:附上源码地址

https://gitee.com/hesson/Dotnetty.Redis.Demo

感谢 @蛀牙 对本文的审阅,并提出修改的建议

原文地址:https://www.cnblogs.com/ruxia/p/9477389.html

时间: 2024-11-05 17:30:39

使用 DotNetty 实现 Redis 的一个控制台应用程序的相关文章

用C#写出一个控制台五子棋程序。学习C#中遇到的比较有意思的程序。

首先,在Main方法中定义棋盘的行数列数,int hang,lie = 15:然后定义数组,string[ , ] qipan = new string[hangshu,lieshu]; 然后写一个下棋的方法  (其中用*代表白棋,#代表黑棋) static void Xiaqi (string[,] qipan,int hang,int lie,bool baizixia) { qipan[hang, lie] = baizixia ? "*":"#"; } ?

asp.net mvc引用控制台应用程序exe

起因:有一个控制台应用程序和一个web程序,web程序想使用exe程序的方法,这个时候就需要引用exe程序. 报错:使用web程序,引用exe程序 ,vs调试没有问题,但是部署到iis就报错,如下: 未能加载文件或程序集“Test.YiXiu”或它的某一个依赖项.试图加载格式不正确的程序. 处理办法: 修改控制台程序的输出方式,修改为[类库] 操作步骤: 在exe项目上右击=>属性=>输出类型,选择 类库

在.NET Core控制台应用程序中使用强类型配置

想象一下,你写一个控制台应用程序,你想要从配置文件中以强类型方式读取配置. .NET Core 可以帮助我们解决. 通常我会在ASP.NET Core MVC中演示,但简单起见,只在控制台应用程序中演示. 让我们创建两个配置类,用于保存应用程序和模块名称. public class MySettings { public string ApplicationName { get; set; } } public class MyModuleSettings { public string Mod

【WCF】创建第一个WCF应用程序

一.什么是WCF: Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,也可以说是一套软件开发包.WCF合并了Web服务..net Remoting.消息队列和Enterprise Services的功能并集成在Visual Studio中.WCF专门用于面向服务开发. WCF的最终目标是通过进程或不同的系统.通过本地网络或是通过Internet收发客户和服务之间的消息.并为服务提供直接的支持.托管.安全.事务管理.离线对立等等

WCF绑定netTcpBinding寄宿到控制台应用程序

契约 新建一个WCF服务类库项目,在其中添加两个WCF服务:GameService,PlayerService 代码如下: [ServiceContract] public interface IGameService { [OperationContract] Task<string> DoWork(string arg); } public class GameService : IGameService { public async Task<string> DoWork(s

完全不借助VS,编写C#控制台应用程序

(因为这个必须要借助控制台,所以必须是控制台应用程序) csc.exe是微软.NET Framework 中的C#编译器 步骤如下: 1)用记事本写一个控制台应用程序的代码,保存在E盘,test.cs using System; namespace Test { class Program { static void Main(string[]args) { Console.WriteLine("生活会更加美好的"); Console.ReadKey(); } } } 2)打开cmd运

.NET Core 控制台应用程序使用异步(Async)Main方法

C# 7.1 及以上的版本允许我们使用异步的Main方法. 一.新建一个控制台应用程序 二.异步Main方法 我们直接将Main方法改为如下: static async Task Main(string[] args) 可以看到报错了,提示我们是C# 7.1 的特性.我们有两种方法可以解决,其实最后都是殊途同归,只是操作不一样而已. 1.第一种方法-修改csproj文件 打开项目的csproj文件,添加如下代码: <PropertyGroup Condition="'$(Configura

如何编写一个编译c#控制台应用程序的批处理程序

如何编写一个编译c#控制台应用程序的批处理程序 2011-03-22 18:14 dc毒蘑菇 | 浏览 579 次 最近在网上看了一个教程,是学C#的,但是我的机子上装不上vs,所以想写一个批处理来编译,因为每次都要我更改目录,然后复制路径,再编译,输出,特别的浪费时间,所以特来求助网友,希望帮帮忙 分享到: 2011-03-22 19:17 #快乐假期,智慧随行# 提问者采纳 不知道你有没有使用过ANT,你可以创建ANT脚本来构建你的应用程序.如果不是很了解,也不愿意编写的话,我介绍你一款可视

分享一个控制台版本《推箱子》小游戏,感兴趣的可以看看

画面党看到这行红字的时候就应该关闭该页面了,没必要看下去. 一个控制台版本的<推箱子>小游戏,感兴趣的可以下来玩一下(包含源代码). 及一个简单的地图编辑器 相关说明在压缩包ReadMe文件中. 源代码下载:http://download.csdn.net/detail/hwenycocodq520/7104973 如果你觉得你设计出的关卡很不错可以发一份给我:e-mail:[email protected] 小程序能有大欢乐么?