博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
阅读量:7187 次
发布时间:2019-06-29

本文共 13946 字,大约阅读时间需要 46 分钟。

  • 参考资料说明

SuperSocket文档 http://docs.supersocket.net/

Protobuf语言参考 https://developers.google.com/protocol-buffers/docs/proto

单消息多类型解决方案 https://developers.google.com/protocol-buffers/docs/techniques#

主要资料(非常感谢) http://www.cnblogs.com/caipeiyu/p/5559112.html

使用的ProtocolBuffers http://code.google.com/p/protobuf-csharp-port

关于MsgPack的协议 https://my.oschina.net/caipeiyu/blog/512437

 

Proto

message CallMessage{    optional string content = 1;}message BackMessage{    optional string content = 1;}message PersonMessage{    required int32 id = 1;    required string name = 2;    enum Sex    {        Male = 1;        Female = 2;    }    required Sex sex = 3 [default = Male];    required uint32 age = 4;    required string phone = 5;}import "BackMessage.proto";import "CallMessage.proto";import "PersonMessage.proto";message DefeatMessage{    enum Type    {        CallMessage = 1;        BackMessage = 2;        PersonMessage = 3;    }    required Type type = 1;    optional CallMessage callMessage = 2;    optional BackMessage backMessage = 3;    optional PersonMessage personMessage = 4;}
View Code

 

生成C#代码

protoc --descriptor_set_out=DefeatMessage.protobin --proto_path=./ --include_imports DefeatMessage.proto protogen DefeatMessage.protobin
View Code

 

Server

namespace SuperSocketProtoServer.Protocol{    public class ProtobufRequestInfo: IRequestInfo    {        public string Key { get; }        public DefeatMessage.Types.Type Type { get; }        public DefeatMessage Body { get; }        public ProtobufRequestInfo(DefeatMessage.Types.Type type, DefeatMessage body)        {            Type = type;            Key = type.ToString();            Body = body;        }    }}namespace SuperSocketProtoServer.Protocol{    public class ProtobufReceiveFilter: IReceiveFilter
, IOffsetAdapter, IReceiveFilterInitializer { private int _origOffset; private int _offsetDelta; private int _leftBufferSize; public void Initialize(IAppServer appServer, IAppSession session) { _origOffset = session.SocketSession.OrigReceiveOffset; } public int OffsetDelta { get { return _offsetDelta; } } ///
/// 数据包解析 /// ///
接收缓冲区 ///
接收到的数据在缓冲区的起始位置 ///
本轮接收到的数据长度 ///
为接收到的数据重新创建一个备份而不是直接使用接收缓冲区 ///
接收缓冲区未被解析的数据 ///
public ProtobufRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest) { rest = 0; // 重新计算缓冲区的起始位置,前一次解析还有剩下没有解析的数据就需要把起始位置移到之前最后要解析的那个位置 var readOffset = offset - _offsetDelta; // 由google.protocolbuffers提供 CodedInputStream cis = CodedInputStream.CreateInstance(readBuffer, readOffset, length); // 计算数据包的长度,不包含Length本身 int varint32 = (int) cis.ReadRawVarint32(); if (varint32 <= 0) return null; // 计算协议里面Length占用字节 int headLen = (int) (cis.Position - readOffset); // 本轮解析完缓冲后剩余没有解析的数据大小 rest = length - varint32 - headLen + _leftBufferSize; // 缓冲里面的数据足够本轮解析 if (rest >= 0) { byte[] body = cis.ReadRawBytes(varint32); DefeatMessage message = DefeatMessage.ParseFrom(body); ProtobufRequestInfo requestInfo = new ProtobufRequestInfo(message.Type, message); _offsetDelta = 0; _leftBufferSize = 0; return requestInfo; } // 缓冲里面的数据不够本次解析[tcp分包传送] else { _leftBufferSize += length; _offsetDelta = _leftBufferSize; rest = 0; var expectedOffset = offset + length; var newOffset = _origOffset + _offsetDelta; if (newOffset < expectedOffset) Buffer.BlockCopy(readBuffer, offset - _leftBufferSize + length, readBuffer, _origOffset, _leftBufferSize); } return null; } public void Reset() { _offsetDelta = 0; _leftBufferSize = 0; } public int LeftBufferSize { get { return _leftBufferSize; } } public IReceiveFilter
NextReceiveFilter { get; } public FilterState State { get; } }}namespace SuperSocketProtoServer.Protocol{ public class ProtobufAppSession:AppSession
{ public ProtobufAppSession() { } }}namespace SuperSocketProtoServer.Protocol{ public class ProtobufAppServer: AppServer
{ public ProtobufAppServer() : base(new DefaultReceiveFilterFactory
()) { } }}
View Code

 

Server Command

 

namespace SuperSocketProtoServer.Protocol.Command{    public class BackMessage : CommandBase
{ public override void ExecuteCommand(ProtobufAppSession session, ProtobufRequestInfo requestInfo) { Console.WriteLine("BackMessage:{0}", requestInfo.Body.BackMessage.Content); } }}namespace SuperSocketProtoServer.Protocol.Command{ public class CallMessage : CommandBase
{ public override void ExecuteCommand(ProtobufAppSession session, ProtobufRequestInfo requestInfo) { Console.WriteLine("CallMessage:{0}", requestInfo.Body.CallMessage.Content); var backMessage = global::BackMessage.CreateBuilder().SetContent("Hello I am from C# server by SuperSocket") .Build(); var message = DefeatMessage.CreateBuilder().SetType(DefeatMessage.Types.Type.BackMessage) .SetBackMessage(backMessage).Build(); using (var stream = new MemoryStream()) { CodedOutputStream cos = CodedOutputStream.CreateInstance(stream); cos.WriteMessageNoTag(message); cos.Flush(); byte[] data = stream.ToArray(); session.Send(new ArraySegment
(data)); } } }}namespace SuperSocketProtoServer.Protocol.Command{ public class PersonMessage:CommandBase
{ public override void ExecuteCommand(ProtobufAppSession session, ProtobufRequestInfo requestInfo) { Console.WriteLine("Recv Person Message From Client."); Console.WriteLine("person's id = {0}, person's name = {1}, person's sex = {2}, person's phone = {3}", requestInfo.Body.PersonMessage.Id, requestInfo.Body.PersonMessage.Name, requestInfo.Body.PersonMessage.Sex, requestInfo.Body.PersonMessage.Phone); } }}
View Code

 

Client

namespace SuperSocketProtoClient.Protocol{    public class ProtobufPackageInfo: IPackageInfo    {        public string Key { get; }        public DefeatMessage.Types.Type Type { get; }        public DefeatMessage Body { get; }        public ProtobufPackageInfo(DefeatMessage.Types.Type type, DefeatMessage body)        {            Type = type;            Key = type.ToString();            Body = body;        }    }}namespace SuperSocketProtoClient.Protocol{    public class ProtobufReceiveFilter: IReceiveFilter
{ ///
/// 数据解析 /// BufferList已经实现了分包处理 /// ///
数据缓冲区 ///
缓冲区剩余数据 public ProtobufPackageInfo Filter(BufferList data, out int rest) { rest = 0; var buffStream = new BufferStream(); buffStream.Initialize(data); var stream = CodedInputStream.CreateInstance(buffStream); var varint32 = (int)stream.ReadRawVarint32(); if (varint32 <= 0) return default(ProtobufPackageInfo); var total = data.Total; var packageLen = varint32 + (int)stream.Position; if (total >= packageLen) { rest = total - packageLen; var body = stream.ReadRawBytes(varint32); var message = DefeatMessage.ParseFrom(body); var requestInfo = new ProtobufPackageInfo(message.Type, message); return requestInfo; } return default(ProtobufPackageInfo); } public void Reset() { NextReceiveFilter = null; State = FilterState.Normal; } public IReceiveFilter
NextReceiveFilter { get; protected set; } public FilterState State { get; protected set; } }}
View Code

 

Server Entrance

namespace SuperSocketProtoServer{    class Program    {        static void Main(string[] args)        {            Console.WriteLine("Press any key to start the server!");            Console.ReadKey();            Console.WriteLine();            var appServer = new ProtobufAppServer();            //appServer.NewRequestReceived += AppServerOnNewRequestReceived;            try            {                //Setup the appServer                if (!appServer.Setup(2017)) //Setup with listening port                {                    Console.WriteLine("Failed to setup!");                    Console.ReadKey();                    return;                }            }catch(Exception e) { Console.WriteLine(e);}            Console.WriteLine();            //Try to start the appServer            if (!appServer.Start())            {                Console.WriteLine("Failed to start!");                Console.ReadKey();                return;            }            Console.WriteLine("The server started successfully, press key 'q' to stop it!");            while (Console.ReadKey().KeyChar != 'q')            {                Console.WriteLine();                continue;            }            //Stop the appServer            appServer.Stop();            Console.WriteLine("The server was stopped!");            Console.ReadKey();        }        private static void AppServerOnNewRequestReceived(ProtobufAppSession session, ProtobufRequestInfo requestinfo)        {            switch (requestinfo.Type)            {                case DefeatMessage.Types.Type.BackMessage:                    Console.WriteLine("BackMessage:{0}", requestinfo.Body.BackMessage.Content);                    break;                case DefeatMessage.Types.Type.CallMessage:                    Console.WriteLine("CallMessage:{0}", requestinfo.Body.CallMessage.Content);                    var backMessage = BackMessage.CreateBuilder().SetContent("Hello I am from C# server by SuperSocket")                        .Build();                    var message = DefeatMessage.CreateBuilder().SetType(DefeatMessage.Types.Type.BackMessage)                        .SetBackMessage(backMessage).Build();                    using (var stream = new MemoryStream())                    {                        CodedOutputStream cos = CodedOutputStream.CreateInstance(stream);                        cos.WriteMessageNoTag(message);                        cos.Flush();                        byte[] data = stream.ToArray();                        session.Send(new ArraySegment
(data)); } break; } } }}
View Code

 

Client Entrance

namespace SuperSocketProtoClient{    class Program    {        static void Main(string[] args)        {            EasyClient client = new EasyClient();            client.Initialize(new ProtobufReceiveFilter(), packageInfo =>            {                switch (packageInfo.Type)                {                    case DefeatMessage.Types.Type.BackMessage:                        Console.WriteLine("BackMessage:{0}", packageInfo.Body.BackMessage.Content);                        break;                    case DefeatMessage.Types.Type.CallMessage:                        Console.WriteLine("CallMessage:{0}", packageInfo.Body.CallMessage.Content);                        break;                }            });            var flag = client.ConnectAsync(new DnsEndPoint("127.0.0.1", 2017));            if (flag.Result)            {                var callMessage = CallMessage.CreateBuilder()                    .SetContent("Hello I am form C# client by SuperSocket ClientEngine").Build();                var message = DefeatMessage.CreateBuilder()                    .SetType(DefeatMessage.Types.Type.CallMessage)                    .SetCallMessage(callMessage).Build();                using (var stream = new MemoryStream())                {                    CodedOutputStream os = CodedOutputStream.CreateInstance(stream);                    os.WriteMessageNoTag(message);                    os.Flush();                    byte[] data = stream.ToArray();                    client.Send(new ArraySegment
(data)); } Thread.Sleep(2000); // 发送PersonMessage var personMessage = PersonMessage.CreateBuilder() .SetId(123).SetAge(33).SetSex(PersonMessage.Types.Sex.Male).SetName("zstudio").SetPhone("110").Build(); message = DefeatMessage.CreateBuilder().SetType(DefeatMessage.Types.Type.PersonMessage) .SetPersonMessage(personMessage).Build(); using (var stream = new MemoryStream()) { CodedOutputStream cos = CodedOutputStream.CreateInstance(stream); cos.WriteMessageNoTag(message); cos.Flush(); byte[] data = stream.ToArray(); client.Send(new ArraySegment
(data)); } } Console.ReadKey(); } }}
View Code
  • 执行结果

  • https://www.cnblogs.com/linxmouse/p/7905575.html
  • 工程、资源、资料打包:http://pan.baidu.com/s/1qXB9aEg
  • 更多项目相关细节和详情参考博客:http://www.cnblogs.com/caipeiyu/p/5559112.html, 在此也表示对博主由衷的感谢!!!

 

你可能感兴趣的文章
路由器不重启,是否ip就永远不变
查看>>
安卓数据传递之---putextra与putextras
查看>>
【后会有期】
查看>>
Codeforces Round #346 (Div. 2) - D Bicycle Race
查看>>
nginx php上传大小设置
查看>>
spring原理机制
查看>>
Oracle分析函数-nulls first/nulls last
查看>>
Python中时间类解决类似朋友圈时间显示的小问题
查看>>
NodeJs针对Express框架配置Mysql进行数据库操作
查看>>
基于MNIST数据集使用TensorFlow训练一个没有隐含层的浅层神经网络
查看>>
Hive修改表
查看>>
Leetcode刷题记录:编码并解码短网址
查看>>
【数据分析】Superset 之四 直接安装
查看>>
Swift - what's the difference between metatype .Type and .self?
查看>>
Matroid[转]
查看>>
Android 实现歌词同步
查看>>
c#设计模式-观察者模式
查看>>
如何使用cocos2d制作基于tile地图的游戏教程:第一部分
查看>>
SVN的版本日期
查看>>
圆满完成性能测试诊断分析与优化周末班培训课程!
查看>>