• SuperSocket与Netty之实现protobuf协议,包括服务端和客户端


    • 参考资料说明

    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<ProtobufRequestInfo>, 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; }
            }
    
            /// <summary>
            /// 数据包解析
            /// </summary>
            /// <param name="readBuffer">接收缓冲区</param>
            /// <param name="offset">接收到的数据在缓冲区的起始位置</param>
            /// <param name="length">本轮接收到的数据长度</param>
            /// <param name="toBeCopied">为接收到的数据重新创建一个备份而不是直接使用接收缓冲区</param>
            /// <param name="rest">接收缓冲区未被解析的数据</param>
            /// <returns></returns>
            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<ProtobufRequestInfo> NextReceiveFilter { get; }
    
            public FilterState State { get; }
        }
    }
    
    namespace SuperSocketProtoServer.Protocol
    {
        public class ProtobufAppSession:AppSession<ProtobufAppSession, ProtobufRequestInfo>
        {
            public ProtobufAppSession() { }
        }
    }
    
    namespace SuperSocketProtoServer.Protocol
    {
        public class ProtobufAppServer: AppServer<ProtobufAppSession, ProtobufRequestInfo>
        {
            public ProtobufAppServer()
                : base(new DefaultReceiveFilterFactory<ProtobufReceiveFilter, ProtobufRequestInfo>())
            {
                
            }
        }
    }
    View Code

    Server Command

    namespace SuperSocketProtoServer.Protocol.Command
    {
        public class BackMessage : CommandBase<ProtobufAppSession, ProtobufRequestInfo>
        {
            public override void ExecuteCommand(ProtobufAppSession session, ProtobufRequestInfo requestInfo)
            {
                Console.WriteLine("BackMessage:{0}", requestInfo.Body.BackMessage.Content);
            }
        }
    }
    
    namespace SuperSocketProtoServer.Protocol.Command
    {
        public class CallMessage : CommandBase<ProtobufAppSession, ProtobufRequestInfo>
        {
            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<byte>(data));
                }
            }
        }
    }
    
    namespace SuperSocketProtoServer.Protocol.Command
    {
        public class PersonMessage:CommandBase<ProtobufAppSession, ProtobufRequestInfo>
        {
            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<ProtobufPackageInfo>
        {
            /// <summary>
            /// 数据解析
            /// BufferList已经实现了分包处理
            /// </summary>
            /// <param name="data">数据缓冲区</param>
            /// <param name="rest">缓冲区剩余数据</param>
            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<ProtobufPackageInfo> 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<byte>(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<byte>(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<byte>(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, 在此也表示对博主由衷的感谢!!!
  • 相关阅读:
    关于sql json数据的处理
    时间函数strtotime的强大
    /usr/bin/install: cannot create regular file `/usr/local/jpeg6/include/jconfig.h'
    linux安装php7.2.7
    关于sql时间方面的处理
    关于centos防火墙的一些问题
    linux 安装ssl 失败原因
    linux安装php7.2.7
    拾取坐标和反查询接口api
    【转】通过点击获取地址等信息、可以传值
  • 原文地址:https://www.cnblogs.com/tianciliangen/p/9187283.html
Copyright © 2020-2023  润新知