BeetleX实现MessagePack和Protobuf消息控制器调用websocket服务详解
最近有用户问如何使用BeetleX封装一个基于Protobuf格式的websocket服务并支持控制器调用;其实BeetleX.FastHttpApi是支持Websocket服务和自定义数据格式的,但需要对组件有一定了解的情况才能进行扩展;接下来通过封装一个支持Protobuf和MessagePack的websocket服务来介绍BeetleX.FastHttpApi的相关功能扩展和应用。
格式封装
首先需要一个特性来标记消息类型在数据中用怎样的数据来标记
[AttributeUsage(AttributeTargets.Class)]public class BinaryTypeAttribute : Attribute{public BinaryTypeAttribute(int id){this.ID = id;}public int ID { get; set; }}
可以通过一个Int类型来定义数值与消息类的关系。然后再定义一个特性用于描述消息对应的方法控制器类
[AttributeUsage(AttributeTargets.Class)]public class MessageControllerAttribute : Attribute{}
接下来就可以封装对应Protobuf的数据转换类,为了转换方便就直接使用Protobuf.Net这个组件了
public class BinaryDataFactory{//数值与类型的关系private Dictionary<int, Type> mMessageTypes = new Dictionary<int, Type>();//类型与数值的关系private Dictionary<Type, int> mMessageIDs = new Dictionary<Type, int>();//定义消息与方法的关系private Dictionary<Type, ActionHandler> mActions = new Dictionary<Type, ActionHandler>();public void RegisterComponent<T>(){RegisterComponent(typeof(T));}public void RegisterComponent(Type type){foreach (var item in type.Assembly.GetTypes()){BinaryTypeAttribute[] bta = (BinaryTypeAttribute[])item.GetCustomAttributes(typeof(BinaryTypeAttribute), false);if (bta != null && bta.Length > 0){mMessageTypes[bta[0].ID] = item;mMessageIDs[item] = bta[0].ID;}
#if PROTOBUF_SERVERvar mca = item.GetCustomAttribute<MessageControllerAttribute>(false);if (mca != null){var controller = Activator.CreateInstance(item);foreach (MethodInfo method in item.GetMethods(BindingFlags.Public | BindingFlags.Instance)){var parameters = method.GetParameters();if (parameters.Length == 2 && parameters[0].ParameterType == typeof(WebSocketReceiveArgs)) {ActionHandler handler = new ActionHandler(controller, method);mActions[parameters[1].ParameterType] = handler;}}}
#endif}}//序列化对象public ArraySegment<byte> Serializable(object data){MemoryStream memory = new MemoryStream();var type = GetTypeID(data.GetType(), true);memory.Write(type);Serializer.Serialize(memory, data);return new ArraySegment<byte>(memory.GetBuffer(), 0, (int)memory.Length);}//反序列化对象public object Deserialize(byte[] data){return Deserialize(new ArraySegment<byte>(data, 0, data.Length));}//反序列化对象public object Deserialize(ArraySegment<byte> data){MemoryStream memory = new MemoryStream(data.Array, data.Offset, data.Count);byte[] id = new byte[4];memory.Read(id, 0, 4);Type type = GetMessageType(id, true);return Serializer.Deserialize(type, memory);}//获消息对应数值的存储数据public byte[] GetTypeID(Type type, bool littleEndian){if (mMessageIDs.TryGetValue(type, out int value)){if (!littleEndian){value = BeetleX.Buffers.BitHelper.SwapInt32(value);}return BitConverter.GetBytes(value);}throw new Exception($"binary websocket {type} id not found!");}//根据数值获取类型public Type GetMessageType(int id){mMessageTypes.TryGetValue(id, out Type result);return result;}//根据存储数获取类型public Type GetMessageType(byte[] data, bool littleEndian){int value = BitConverter.ToInt32(data, 0);if (!littleEndian)value = BeetleX.Buffers.BitHelper.SwapInt32(value);return GetMessageType(value);}//根据消息获处理方法public ActionHandler GetHandler(object message){mActions.TryGetValue(message.GetType(), out ActionHandler result);return result;}}public class ActionHandler{public ActionHandler(object controller, MethodInfo method){Method = method;Controller = controller;IsVoid = method.ReturnType == typeof(void);IsTaskResult = method.ReturnType.BaseType == typeof(Task);if (IsTaskResult && method.ReturnType.IsGenericType){HasTaskResultData = true;mGetPropertyInfo = method.ReturnType.GetProperty("Result", BindingFlags.Public | BindingFlags.Instance);}}private PropertyInfo mGetPropertyInfo;public MethodInfo Method { get; private set; }public bool IsTaskResult { get; set; }public bool HasTaskResultData { get; set; }public bool IsVoid { get; private set; }public object Controller { get; private set; }public object GetTaskResult(Task task){return mGetPropertyInfo.GetValue(task);}public object Execute(params object[] data){return Method.Invoke(Controller, data);}}
协议转换
Protobuf的数据格式处理完成后就要针对BeetleX.FastHttpApi构建一个相应数据转换器,这个转换器实现也是非常简单的。
public class ProtobufFrameSerializer : IDataFrameSerializer{public static BinaryDataFactory BinaryDataFactory { get; set; } = new BinaryDataFactory();public object FrameDeserialize(DataFrame data, PipeStream stream){var buffers = new byte[data.Length];stream.Read(buffers, 0, buffers.Length);return BinaryDataFactory.Deserialize(buffers);}public void FrameRecovery(byte[] buffer){}public ArraySegment<byte> FrameSerialize(DataFrame packet, object body){return BinaryDataFactory.Serializable(body);}}
实现IDataFrameSerializer接口的相关方法即可,接下来就针对HttpApiServer封装一些扩展方法用于注册对象和绑定Websocket数据处理事件。
public static class ProtobufExtensions{public static HttpApiServer RegisterProtobuf<T>(this HttpApiServer server){ProtobufFrameSerializer.BinaryDataFactory.RegisterComponent<T>();return server;}public static HttpApiServer UseProtobufController(this HttpApiServer server, Action<WebSocketReceiveArgs> handler = null){server.WebSocketReceive = async (o, e) =>{try{var msg = e.Frame.Body;var action = ProtobufFrameSerializer.BinaryDataFactory.GetHandler(msg);if (action != null){if (!action.IsVoid){if (action.IsTaskResult){Task task = (Task)action.Execute(e, msg);await task;if (action.HasTaskResultData){var result = action.GetTaskResult(task);e.ResponseBinary(result);}}else{var result = action.Execute(e, msg);e.ResponseBinary(result);}}}else{handler?.Invoke(e);}}catch (Exception e_){e.Request.Server.GetLog(BeetleX.EventArgs.LogType.Warring)?.Log(BeetleX.EventArgs.LogType.Error, e.Request.Session, $"Websocket packet process error {e_.Message}");}};return server;}}
服务使用
所有相关功能都封装后就可以启动HttpApiServer并把相关功能设置使用,在使用之前需要引用BeetleX.FastHttpApi.Hosting和Protobuf.Net组件
[BinaryType(1)][ProtoContract]public class User{[ProtoMember(1)]public string Name { get; set; }[ProtoMember(2)]public string Email { get; set; }[ProtoMember(3)]public DateTime ResultTime { get; set; }}[MessageController]public class Controller{public User Login(WebSocketReceiveArgs e, User user){user.ResultTime = DateTime.Now;return user;}}class Program{static void Main(string[] args){BeetleX.FastHttpApi.Hosting.HttpServer server = new BeetleX.FastHttpApi.Hosting.HttpServer(80);server.Setting((service, options) =>{options.LogLevel = BeetleX.EventArgs.LogType.Trace;options.LogToConsole = true;options.ManageApiEnabled = false;options.WebSocketFrameSerializer = new ProtobufFrameSerializer();});server.Completed(http =>{http.RegisterProtobuf<User>();http.UseProtobufController();});server.Run();}}
客户端
如果你希望在.Net中使用Websocket客户端,BeetleX同样也提供一个扩展组件BeetleX.Http.Clients,通过这组件可以轻易封装一个Protobuf的Websocket客户端。
public class ProtobufClient : BinaryClient{public ProtobufClient(string host) : base(host) { }public static BinaryDataFactory BinaryDataFactory { get; private set; } = new BinaryDataFactory();protected override object OnDeserializeObject(ArraySegment<byte> data){return BinaryDataFactory.Deserialize(data);}protected override ArraySegment<byte> OnSerializeObject(object data){return BinaryDataFactory.Serializable(data);}}
封装完成后就可以使用了
class Program{static async Task Main(string[] args){ProtobufClient.BinaryDataFactory.RegisterComponent<User>();var client = new ProtobufClient("ws://localhost");User user = new User { Email="henryfan@msn.com", Name="henryfan" };var result = await client.ReceiveFrom<User>(user);Console.WriteLine(result.Name);}}
完整示例代码可以访问
https://github.com/beetlex-io/BeetleX-Samples/tree/master/Websocket.ProtobufPacket
https://github.com/beetlex-io/BeetleX-Samples/tree/master/Websocket.MessagePackPacket
BeetleX
开源跨平台通讯框架(支持TLS)
提供高性能服务和大数据处理解决方案
https://beetlex-io.com
BeetleX实现MessagePack和Protobuf消息控制器调用websocket服务详解相关推荐
- java有返回值的方法回调_java调用回调机制详解
调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...
- Spring MVC 学习总结(二)——控制器定义与@RequestMapping详解
Spring MVC 学习总结(二)--控制器定义与@RequestMapping详解 目录 一.控制器定义 1.1.实现接口Controller定义控制器 1.2.使用注解@Controller定义 ...
- Python 在子类中调用父类方法详解(单继承、多层继承、多重继承)
Python 在子类中调用父类方法详解(单继承.多层继承.多重继承) by:授客 QQ:1033553122 测试环境: win7 64位 Python版本:Python 3.3.5 代码实践 ...
- php把proto解析为文档,Protobuf 文件生成工具 Prototool 命令详解
Protobuf 文件生成工具 Prototool 命令详解 简介 Prototool 是 Protobuf 文件的生成工具, 目前支持go, php, java, c#, object c 五种语言 ...
- Kubernetes K8S之资源控制器Job和CronJob详解
Kubernetes的资源控制器Job和CronJob详解与示例 主机配置规划 服务器名称(hostname) 系统版本 配置 内网IP 外网IP(模拟) k8s-master CentOS7.7 2 ...
- 微信公众号图文消息添加word附件教程详解
微信公众号图文消息添加word附件教程详解 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序"微附件"进行在 ...
- html5 调用手机摄像头详解
html5 调用手机摄像头详解 首先,我们看看HTML代码结构,当然,这部分的DOM内容应该是在用户允许使用其摄像头事件出发后,动态加载生成的. 注意: 我们采用的是 640X480的分辨率,如 ...
- vue中如何调用ios摄像头_vue2.0调用摄像头步骤详解
这次给大家带来vue2.0调用摄像头步骤详解,使用vue2.0调用摄像头的注意事项有哪些,下面就是实战案例,一起来看一下. 可以在github 上下载demo链接 vue组件代码 import {Ex ...
- 消息队列RabbitMQ基础知识详解
一: 什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序或者模块对模块的通信方法.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写入消息,而另 ...
最新文章
- 2022-2028年中国汽车零部件行业市场研究及前瞻分析报告
- 图像拼接--Automatic Panoramic Image Stitching using Invariant Features
- Android PathDashPathEffect 使用详解
- android studio插件提升工作效率
- Linux基本管理七大流程
- java中的DAO设计模式
- 【转】Tcl/Tk 漫谈
- jbox弹窗_关于使用 jBox 对话框的提交不能弹出问题解决方法
- html基础 — area(图像的作用区域标记)
- 让div不占位置_开箱测评户外折叠桌椅,收纳起来真的不占位置,强行不血亏啊!...
- 现代通信技术之交换技术基础
- 计算机CAD作文,CAD:电脑系统字体和CAD字体的区别
- 激活工具带毒,静默安装360、2345系列软件
- 一位女性程序员的职业规划
- 计算机网络安全叙述,计算机网络安全涉及
- Lucene关键字高亮显示
- 高手如何做全网整合营销推广?全网营销方法和策略有哪些?
- 分享一个盟重英雄的辅助脚本工具
- 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
- 12.16 Day 1 - 分布式系统架构的冰与火
热门文章
- memwatch内存泄露检测工具
- C#利用Socket实现客户端之间直接通信
- 第二十四天 多维数组
- vim 离线安装_VIM学习笔记 插件列表(Plugin)
- 用jQuery实现弹出窗口/弹出div层
- javascript 控制弹出窗口
- AddTransient、AddSingleton、AddScoped 三者都应该在什么场景下使用
- Generative Adversarial Learning Towards Fast Weakly Supervised Detection(CVPR2018)阅读笔记
- HTTP2和HTTPS来不来了解一下?
- 业务id转密文短链的一种实现思路