• 参考资料说明

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 = ;
} message BackMessage
{
optional string content = ;
} message PersonMessage
{
required int32 id = ;
required string name = ;
enum Sex
{
Male = ;
Female = ;
}
required Sex sex = [default = Male];
required uint32 age = ;
required string phone = ;
} import "BackMessage.proto";
import "CallMessage.proto";
import "PersonMessage.proto"; message DefeatMessage
{
enum Type
{
CallMessage = ;
BackMessage = ;
PersonMessage = ;
}
required Type type = ;
optional CallMessage callMessage = ;
optional BackMessage backMessage = ;
optional PersonMessage personMessage = ;
}
  • 生成C#代码
 protoc --descriptor_set_out=DefeatMessage.protobin --proto_path=./ --include_imports DefeatMessage.proto
protogen DefeatMessage.protobin
  • 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 = ;
// 重新计算缓冲区的起始位置,前一次解析还有剩下没有解析的数据就需要把起始位置移到之前最后要解析的那个位置
var readOffset = offset - _offsetDelta;
// 由google.protocolbuffers提供
CodedInputStream cis = CodedInputStream.CreateInstance(readBuffer, readOffset, length);
// 计算数据包的长度,不包含Length本身
int varint32 = (int) cis.ReadRawVarint32();
if (varint32 <= ) return null; // 计算协议里面Length占用字节
int headLen = (int) (cis.Position - readOffset);
// 本轮解析完缓冲后剩余没有解析的数据大小
rest = length - varint32 - headLen + _leftBufferSize; // 缓冲里面的数据足够本轮解析
if (rest >= )
{
byte[] body = cis.ReadRawBytes(varint32);
DefeatMessage message = DefeatMessage.ParseFrom(body);
ProtobufRequestInfo requestInfo = new ProtobufRequestInfo(message.Type, message);
_offsetDelta = ;
_leftBufferSize = ; return requestInfo;
}
// 缓冲里面的数据不够本次解析[tcp分包传送]
else
{
_leftBufferSize += length;
_offsetDelta = _leftBufferSize;
rest = ; 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 = ;
_leftBufferSize = ;
} 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>())
{ }
}
}
  • 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);
}
}
}
  • 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 = ;
var buffStream = new BufferStream();
buffStream.Initialize(data); var stream = CodedInputStream.CreateInstance(buffStream);
var varint32 = (int)stream.ReadRawVarint32();
if (varint32 <= ) 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; }
}
}
  • 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()) //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;
}
}
}
}
  • 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", ));
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(); // 发送PersonMessage
var personMessage = PersonMessage.CreateBuilder()
.SetId().SetAge().SetSex(PersonMessage.Types.Sex.Male).SetName("zstudio").SetPhone("").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();
}
}
}
  • 执行结果

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

SuperSocket与SuperSocket.ClientEngine实现Protobuf协议的更多相关文章

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

    今天准备给大家介绍一个c#服务器框架(SuperSocket)和一个c#客户端框架(SuperSocket.ClientEngine).这两个框架的作者是园区里面的江大渔. 首先感谢他的无私开源贡献. ...

  2. SuperSocket入门(五)-常用协议实现模版及FixedSizeReceiveFilter示例

             Socket里面的协议解析是Socket通讯程序设计中最复杂的地方,如果你的应用层协议设计或实现不佳,Socket通讯中常见的粘包,分包就难以避免.SuperSocket内置了命令行 ...

  3. SuperSocket内置的命令行协议

    内置的命令行协议(接受自定义,分隔符为“:”,“,”): 命令行协议定义了每个请求必须以回车换行结尾 "\r\n". 由于 SuperSocket 中内置的命令行协议用空格来分割请 ...

  4. Google的Protobuf协议分析

    protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...

  5. netty 对 protobuf 协议的解码与包装探究(2)

    netty 默认支持protobuf 的封装与解码,如果通信双方都使用netty则没有什么障碍,但如果客户端是其它语言(C#)则需要自己仿写与netty一致的方式(解码+封装),提前是必须很了解net ...

  6. 使用Go语言+Protobuf协议完成一个多人聊天室

    软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...

  7. 自定义兼容多种Protobuf协议的编解码器

    <从零开始搭建游戏服务器>自定义兼容多种Protobuf协议的编解码器 直接在protobuf序列化数据的前面,加上一个自定义的协议头,协议头里包含序列数据的长度和对应的数据类型,在数据解 ...

  8. Protobuf 协议语言指南

    l  定义一个消息(message)类型 l  标量值类型 l  Optional 的字段及默认值 l  枚举 l  使用其他消息类型 l  嵌套类型 l  更新一个消息类型 l  扩展 l  包(p ...

  9. Protobuf协议的Java应用例子

    Protobuf协议,全称:Protocol Buffer 它跟JSON,XML一样,是一个规定好的数据传播格式.不过,它的序列化和反序列化的效率太变态了…… 来看看几张图你就知道它有多变态. Pro ...

随机推荐

  1. 一.pycharm配置Pyqt开发环境

    1.安装pyqt pip install PyQt5 pip install PyQt5-tools 2.配置pycharm 2.1 配置QtDesigner Qt Designer 就是一个前端界面 ...

  2. JAVA方法中参数到底是值传递还是引用传递

    当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...

  3. 《操作系统真象还原》BIOS

    以下是读本书第二章的收获. 记得我大学学习操作系统的时候会遇到一些奇奇怪怪的问题,因为觉得问题太奇怪了,所以羞于问老师.诸如ROM到底是个什么东西:如果用内存映射的方式访问外部设备,是不是内存条里专门 ...

  4. 谈一谈php反序列化

    1.序列化与反序列化 php中有两个函数serialize()和unserialize() 序列化serialize(): 当在php中创建了一个对象后,可以通过serialize()把这个对象转变成 ...

  5. SqlServer该如何合适的索引

    SQL索引在数据库优化中占有一个非常大的比例, 一个好的索引的设计,可以让你的效率提高几十甚至几百倍,在这里将带你一步步揭开他的神秘面纱. 1.1 什么是索引? SQL索引有两种,聚集索引和非聚集索引 ...

  6. ubuntu 部署Django项目+uwsgi+Nginx

    1.部署框架 Nginx负责静态资源请求,并且把无法处理的请求转发至uwsgi处理 2.安装并配置Nginx 2.1安装 apt-get install nginx (如果安装失败请先升级apt-ge ...

  7. 使用ADO.NET 查询和操作数据

    一.使用StringBuilder类追加和删除字符串 1.创建StringBuilder类的对象 StringBuilder sb=new StringBuilder("初始字符串值&quo ...

  8. .NetCore学习笔记:二、基于Dapper的泛型Repository

    为减少代码量,这里实现一个基于Dapper的泛型Repository. 这里需要引用Dapper.dll和Dapper.Contrib.dll. 接口定义: /// <summary> / ...

  9. 851. spfa求最短路(spfa算法模板)

    给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你求出1号点到n号点的最短距离,如果无法从1号点走到n号点,则输出impossible. 数据保证不存在负权回路. 输入格式 ...

  10. 安装sublime简易笔记

    1.安装编码工具sublime text3(下载地址,软件管家网盘) 2.安装完成后,下载sublime text3中与python相关的插件:package control 进入package co ...