Beetle在Tcp通讯中使用Protobuf

Beetle在Tcp通讯中使用Protobuf

  Protobuf是google制定的一种对象序列化格式,而在.net下的实现有protobuf-net.而protobuf-net在序列化方面有着出色的性能,效率是.net二进制序列化几倍,而序列化后所占的空间也少于.net二进制序列化;除了以上两个优势外Protobuf有着一个更大的优势就是和其他平台交互的兼容性,在现有大部分流行的语言平台中基本都有Protobuf的实现.因此采用protobuf进行对象序列化是个不错的选择.接下来详细讲解Beetle实现对protobuf-net支持.

定义协议格式

为了保证TCP数据流正确处理,首先要做的事情还是要制定一个处理协议,来保证数据处理的有效性.

数据包同样也是两部分组件,头描述消息总长度,消息体是主内容.由于Protobuf-net序列化的数据交不包括消息类型,所以在协议中必须包括类型名称用于提供给对方实始化对应的类型过行反序列化操作.

实现具体的分析器和消息适配器

协议制定后就可以进行分析器的实现,由于采用头4字节描述大小,所以分析器从HeadSizeOfPackage基础类派生下载重写相关方法即可;完整实现代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using  System;
using  System.Collections.Generic;
using  System.Text;
using  Beetle;
namespace  Beetle.Packages
{
     public  class  ProtobufPackage : Beetle.HeadSizeOfPackage
     {
 
         public  ProtobufPackage()
         {
         }
 
         public  ProtobufPackage(Beetle.TcpChannel channel)
             : base (channel)
         {
 
         }
 
         protected  override  void  WriteMessageType(IMessage msg, BufferWriter writer)
         {
 
         }
 
         public  override  object  WriteCast( object  message)
         {
             MessageAdapter ma = new  MessageAdapter();
             ma.Message = message;
             return  ma;
         }
 
         public  override  object  ReadCast( object  message)
         {
             return  ((MessageAdapter)message).Message;
         }
 
         protected  override  IMessage ReadMessageByType(BufferReader reader, out  object  typeTag)
         {
             typeTag = "ProtobufAdapter" ;
             return  new  MessageAdapter();
         }
 
         static  Dictionary< string , Type> mTypes = new  Dictionary< string , Type>(256);
 
         static  Dictionary<Type, string > mNames = new  Dictionary<Type, string >(256);
 
         public  static  void  LoadAssembly()
         {
             try
             {
                 string  path = AppDomain.CurrentDomain.DynamicDirectory;
                 LoadAssembly(path);
                 path = AppDomain.CurrentDomain.BaseDirectory;
                 LoadAssembly(path);
             }
             catch
             {
             }
         }
 
         public  static  void  LoadAssembly( string  path)
         {
             if  (! string .IsNullOrEmpty(path))
             {
                 foreach  ( string  item in  System.IO.Directory.GetFiles(path, "*.dll" ))
                 {
                     try
                     {
                         LoadAssembly(System.Reflection.Assembly.LoadFile(item));
                     }
                     catch
                     {
                     }
                 }
             }
         }
 
         public  static  void  LoadAssembly(System.Reflection.Assembly assembly)
         {
             foreach  (Type t in  assembly.GetTypes())
             {
                 ProtoBuf.ProtoContractAttribute[] pc = Smark.Core.Functions.GetTypeAttributes<ProtoBuf.ProtoContractAttribute>(t, false );
                 if  (pc.Length > 0)
                 {
                     string  name = t.Name;
                     if  (! string .IsNullOrEmpty(pc[0].Name))
                         name = pc[0].Name;
                     mTypes.Add(name, t);
                     mNames.Add(t, name);
                 }
             }
         }
 
         class  MessageAdapter : Beetle.IMessage
         {
             public  object  Message
             {
                 get ;
                 set ;
             }
 
             public  void  Load(Beetle.BufferReader reader)
             {
                 string  type = reader.ReadString();
                 Beetle.ByteArraySegment segment = ByteArraySegment;
                 reader.ReadByteArray(segment);
                 using  (System.IO.Stream stream = new  System.IO.MemoryStream(segment.Array, 0, segment.Count))
                 {
                     Message = ProtoBuf.Meta.RuntimeTypeModel.Default.Deserialize(stream, null , mTypes[type]);
                 }
 
             }
 
             public  void  Save(Beetle.BufferWriter writer)
             {
                 writer.Write(mNames[Message.GetType()]);
                 Beetle.ByteArraySegment segment = ByteArraySegment;
                 using  (System.IO.Stream stream = new  System.IO.MemoryStream(segment.Array))
                 {
                     ProtoBuf.Meta.RuntimeTypeModel.Default.Serialize(stream, Message);
                     segment.SetInfo(0, ( int )stream.Position);
 
                 }
                 writer.Write(segment);
 
 
             }
 
             [ThreadStatic]
             private  static  ByteArraySegment mByteArraySegment = null ;
 
             public  ByteArraySegment ByteArraySegment
             {
                 get
                 {
                     if  (mByteArraySegment == null )
                         mByteArraySegment = new  ByteArraySegment(TcpUtils.DataPacketMaxLength);
                     return  mByteArraySegment;
                 }
             }
 
         }
     }
}

分析主要重写了ReadCast,WriteCast,而两个Cast方法主要是把消息进行一个适配器包装到一个IMessage对象中提供给组件处理.通过适配器MessageAdapter来实现终于对象的序列化和反序列化操作,并整合的流中.为了方便处理消息对应称名称还添加了分析程序类型来加载对应的类型和名称关系映射.接下来订制一个简单的注册对象

?
1
2
3
4
5
6
7
8
9
10
[ProtoContract]
public  class  Register
{
     [ProtoMember(1)]
     public  string  UserName { get ; set ; }
      [ProtoMember(2)]
     public  string  EMail { get ; set ; }
      [ProtoMember(3)]
     public  DateTime ResponseTime { get ; set ; }
}

实现相应的TCP服务

协议分析器扩展完成后就通过它来实现一个基于protobuf对象处理的TCP交互服务.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class  Program : Beetle.ServerBase<Beetle.Packages.ProtobufPackage>
{
     static  void  Main( string [] args)
     {
         Beetle.Packages.ProtobufPackage.LoadAssembly( typeof (Program).Assembly);
         Beetle.TcpUtils.Setup( "beetle" );
         Program server = new  Program();
         server.Open(9034);
         Console.WriteLine( "server start @9034" );
         Console.Read();
     }
 
     protected  override  void  OnConnected( object  sender, Beetle.ChannelEventArgs e)
     {
         base .OnConnected(sender, e);
         Console.WriteLine( "{0} connected" , e.Channel.EndPoint);
     }
     protected  override  void  OnDisposed( object  sender, Beetle.ChannelDisposedEventArgs e)
     {
         base .OnDisposed(sender, e);
         Console.WriteLine( "{0} disposed" , e.Channel.EndPoint);
     }
     protected  override  void  OnError( object  sender, Beetle.ChannelErrorEventArgs e)
     {
         base .OnError(sender, e);
         Console.WriteLine( "{0} error {1}" , e.Channel.EndPoint, e.Exception.Message);
     }
     protected  override  void  OnMessageReceive(Beetle.PacketRecieveMessagerArgs e)
     {
         Messages.Register register = (Messages.Register)e.Message;
         register.ResponseTime = DateTime.Now;
         e.Channel.Send(register);
     }
}

TCP服务的实现和原来的实现方式一致,只是继承的ServerBase的泛型参是基于protobuf的协议分析器类型.

连接到服务进行对象通讯

同样接入服务端的代码只是改变一下泛型参类型即可

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private  void  cmdConnect_Click( object  sender, EventArgs e)
{
     try
     {
         channel = Beetle.TcpServer.CreateClient<Beetle.Packages.ProtobufPackage>(txtIPAddress.Text, 9034, OnReceive);
         channel.ChannelDisposed += OnDisposed;
         channel.ChannelError += OnError;
         channel.BeginReceive();
         cmdRegister.Enabled = true ;
         cmdConnect.Enabled = false ;
     }
     catch  (Exception e_)
     {
         MessageBox.Show(e_.Message);
     }
}
private  void  OnReceive(Beetle.PacketRecieveMessagerArgs e)
{
     Register reg = (Register)e.Message;
     Invoke( new  Action<Register>(r =>
     {
         txtREMail.Text = r.EMail;
         txtRName.Text = r.UserName;
         txtResponseTime.Text = r.ResponseTime.ToString();
     }), reg);
}
private  void  OnDisposed( object  sender, Beetle.ChannelEventArgs e)
{
     Invoke( new  Action<Beetle.ChannelEventArgs>(s =>
     {
         txtStatus.Text = "disconnect!" ;
         cmdRegister.Enabled = false ;
         cmdConnect.Enabled = true ;
     }), e);
}
private  void  OnError( object  sender, Beetle.ChannelErrorEventArgs e)
{
     Invoke( new  Action<Beetle.ChannelErrorEventArgs>(r =>
     {
         txtStatus.Text = r.Exception.Message;
     }), e);
}

把对象发送给服务端

?
1
2
3
4
Register register = new  Register();
register.EMail = txtEMail.Text;
register.UserName = txtName.Text;
channel.Send(register);

运行效果

下载代码:Code

总结

由于Protobuf制定的协议是开放的,所以很多平台下都有相关实现包括:c++,java,php等.通过整合protobuf作为协议载体可以方便地和其他平台进行TCP数据交互整合.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值