SuperSocket与SuperSocket.ClientEngine实现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 = ;
} 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协议的更多相关文章
- SuperSocket与Netty之实现protobuf协议,包括服务端和客户端
今天准备给大家介绍一个c#服务器框架(SuperSocket)和一个c#客户端框架(SuperSocket.ClientEngine).这两个框架的作者是园区里面的江大渔. 首先感谢他的无私开源贡献. ...
- SuperSocket入门(五)-常用协议实现模版及FixedSizeReceiveFilter示例
Socket里面的协议解析是Socket通讯程序设计中最复杂的地方,如果你的应用层协议设计或实现不佳,Socket通讯中常见的粘包,分包就难以避免.SuperSocket内置了命令行 ...
- SuperSocket内置的命令行协议
内置的命令行协议(接受自定义,分隔符为“:”,“,”): 命令行协议定义了每个请求必须以回车换行结尾 "\r\n". 由于 SuperSocket 中内置的命令行协议用空格来分割请 ...
- Google的Protobuf协议分析
protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...
- netty 对 protobuf 协议的解码与包装探究(2)
netty 默认支持protobuf 的封装与解码,如果通信双方都使用netty则没有什么障碍,但如果客户端是其它语言(C#)则需要自己仿写与netty一致的方式(解码+封装),提前是必须很了解net ...
- 使用Go语言+Protobuf协议完成一个多人聊天室
软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...
- 自定义兼容多种Protobuf协议的编解码器
<从零开始搭建游戏服务器>自定义兼容多种Protobuf协议的编解码器 直接在protobuf序列化数据的前面,加上一个自定义的协议头,协议头里包含序列数据的长度和对应的数据类型,在数据解 ...
- Protobuf 协议语言指南
l 定义一个消息(message)类型 l 标量值类型 l Optional 的字段及默认值 l 枚举 l 使用其他消息类型 l 嵌套类型 l 更新一个消息类型 l 扩展 l 包(p ...
- Protobuf协议的Java应用例子
Protobuf协议,全称:Protocol Buffer 它跟JSON,XML一样,是一个规定好的数据传播格式.不过,它的序列化和反序列化的效率太变态了…… 来看看几张图你就知道它有多变态. Pro ...
随机推荐
- API接口幂等性设计
目录 幂等性场景 解决方案 幂等性场景 网络延迟导致多次重复提交. 表单重复提交. 解决方案 每次提交都使用一个Token,Token保证临时且唯一即可 token生成规则(单机应用):token+U ...
- D - Counting Squares
Your input is a series of rectangles, one per line. Each rectangle is specified as two points(X,Y) t ...
- Git的学习和使用
1.1. Git 了解git的仓库概念 熟悉何为版本控制,了解分布式版本控制(git)和集中式版本控制(svn) 能够熟练使用git的基本指令完成仓库的初始化/添加/提交/日志/回退/分支等操作 gi ...
- linux服务基础之nginx配置详解
nginx简单介绍:https://www.cnblogs.com/ckh2014/p/10848670.html nginx编译安装:https://www.cnblogs.com/ckh2014/ ...
- Spark学习之路 (二十)SparkSQL的元数据[转]
概述 SparkSQL 的元数据的状态有两种: 1.in_memory,用完了元数据也就丢了 2.hive , 通过hive去保存的,也就是说,hive的元数据存在哪儿,它的元数据也就存在哪儿. 换句 ...
- P2710 数列[fhq treap]
调了一辈子的fhq treap- 如果不会最大子段和 如果不会fhq treap 7个操作- 其中三个查询 单点查询其实可以和区间查询写成一个( fhq treap 的修改操作大概就是 \(split ...
- python3练习100题——004
继续做题-经过python3的测试 原题链接:http://www.runoob.com/python/python-exercise-example4.html 题目:输入某年某月某日,判断这一天是 ...
- 在linux中安装nginx
linux系统安装在vmware中,首先在主机中利用shell工具与虚拟机连接 1.在linux中查看虚拟机的ip地址 在终端输入 ifconfig 红框里面就是ip地址 2.在主机中打开shell工 ...
- UI中class的用法:
easyui的引入:<link rel="stylesheet" type="text/css" href="easyui/themes/def ...
- 853. 有边数限制的最短路(Bellman-ford算法模板)
给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你求出从1号点到n号点的最多经过k条边的最短距离,如果无法从1号点走到n号点,输出impossible. 注意:图中可能 存 ...