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)
提供高性能服务和大数据处理解决方案

5a330301cd4513ca7701aa8996c0969d.png

https://beetlex-io.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue 获取url地址的参数_2020年 vue常见面试问题总结(干货)!

1.什么是mvvm模式&#xff0c;谈谈你的理解&#xff1f; MVVM - Model View ViewModel&#xff0c;数据&#xff0c;视图&#xff0c;视图模型view 可以通过 事件绑定 的方式影响 model&#xff0c;model 可以通过 数据绑定 的形式影响到view&#xff0c;viewModel是把 model 和…

CSS 定位之绝对与相对

static,relative,absolute,fixed含义 static(静态定位):元素框正常生成。块级元素生成一个矩形框&#xff0c;作为文档流的的一部分&#xff0c;行内元素则会常见一个或多个行框&#xff0c;至于其父元素中。默认值。没有定位&#xff0c;元素出现在正常的流中&#xff08;忽略…

[第二篇]如何在ASP.Net Core的生产环境中使用OAuth保护swagger ui

在我上篇文章如何在ASP.Net Core的生产环境中保护swagger ui中&#xff0c;我们讨论了如何使用基本身份验证来保护 swagger ui。使用 OAuth 2.0 和 OpenIdConnect 进行保护随着应用程序越来越多地使用 OAuth 和 OpenIdConnect&#xff0c;应用程序很有可能使用 OAuth 和 OpenID…

python opencv 图像切割_【OpenCV+Python】图像的基本操作与算术运算

图像的基本操作在上个教程中&#xff0c;我们介绍了使用鼠标画笔的功能。本次教程&#xff0c;我们将要谈及OpenCV图像处理的基本操作。本次教程的所有操作基本上都和Numpy相关&#xff0c;而不是与OpenCV相关。要使用OpenCV编写更好的优化代码&#xff0c;需要Numpy的丰富知识…

光伏领跑者火热前行 可靠性护航“长跑”

随着第三批光伏领跑者申报标准的出台&#xff0c;在目前普通电站指标有可能缩水的情况下&#xff0c;2017年8-10GW的光伏领跑者项目又将成为各电站投资商争夺的“红海”。光伏领跑者在过去两年时间里为行业带来的变化有目共睹&#xff0c;从模式创新到电价下降&#xff0c;快速…

鸿蒙os系统被推送,鸿蒙来了!华为大规模推送鸿蒙OS系统,造成网站一度瘫痪...

千呼万唤始出来&#xff0c;期盼已久的手机鸿蒙OS系统终于迎来了大规模推送&#xff01;今年2月份在华为Mate X2折叠屏手机发布会上&#xff0c;华为就曾表示将在4月份开始大规模推送鸿蒙OS系统&#xff0c;4月27日通过测试申请的用户正式接到升级鸿蒙OS系统的通知&#xff0c;…

jps、jinfo、jstat、jstack、jmap、jconsole等命令简介

2019独角兽企业重金招聘Python工程师标准>>> JDK提供了几个很实用的工具&#xff0c;如下&#xff1a; jinfo&#xff1a;观察运行中的java程序的运行环境参数&#xff1a;参数包括Java System属性和JVM命令行参数&#xff0c;java class path等信息。命令格式&…

读《底层逻辑》

《底层逻辑》本书作者是刘润&#xff0c;他在得到上开设了课程《5 分钟商学院》&#xff0c;我也是在得到上知道的这本书&#xff0c;这本书在豆瓣上的评分不是很高&#xff0c;褒贬不一&#xff0c;不过我看着觉得挺好。看了一些豆瓣的评论&#xff0c;觉得这本书不好有这么几…

2016年:勒索病毒造成损失预估超过10亿美元

根据趋势科技公布的最新报告&#xff08;PDF&#xff09;&#xff0c;2016年是敲诈勒索软件频发的一年&#xff0c;同比增长752%&#xff0c;预测由Locky、Goldeneye等勒索病毒所造成的损失超过10亿美元。报告中同时指出企业和个人是勒索软件的重灾区&#xff0c;而且勒索病毒还…

python3.6字典有序_为什么从Python 3.6开始字典有序并效率更高

在Python 3.5&#xff08;含&#xff09;以前&#xff0c;字典是不能保证顺序的&#xff0c;键值对A先插入字典&#xff0c;键值对B后插入字典&#xff0c;但是当你打印字典的Keys列表时&#xff0c;你会发现B可能在A的前面。 但是从Python 3.6开始&#xff0c;字典是变成有顺序…

Linux的进程/线程间通信方式总结

2019独角兽企业重金招聘Python工程师标准>>> Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程通信方式&#xff1a;包括信号量(Semaphore), 消息队列(Me…

开源作者去世后,代码谁来继承?

文 | 肖滢出品 | OSC开源社区&#xff08;ID&#xff1a;oschina2013)2008 年初&#xff0c;澳大利亚一对兄弟 Simon Zerner 和 Toby Zerner 开始了 esoTalk 的开发。不幸的是&#xff0c; esoTalk 尚处于 Alpha 阶段&#xff0c;主力开发人员哥哥 Simon 就在 2009 年年中去世。…

项目中使用CLR编程

1、创建自己的项目 2、右键“解决方案。。。”→添加→新建项目→C#→数据库→SQL Server项目,如下图所示: 3、选择操作数据库

SDN火爆!未来五年年复合增长率达98%

在如今的网络世界&#xff0c;软件定义网络SDN和网络功能虚拟化NFV成为了新的“宠儿”&#xff0c;特别是对于运营商来说&#xff0c;已经将它们视为面向未来转型的关键。因此已经有越来越多的运营商开始尝试引入SDN和NFV技术&#xff0c;尽管它们的标准还尚未完善。 最早采用S…

python桌面程序开发_程序员之路:python3+PyQt5+pycharm桌面GUI开发

先看效果&#xff1a;图 1 没错&#xff0c;学过C#的同学应该很熟悉这个界面&#xff0c;按钮风格和界面风格很相似&#xff0c;万万没想到&#xff0c;python也可以做出这样的界面&#xff0c;简直了&#xff01;&#xff08;图 1&#xff09; 正文开始 一、安装python 为啥要…

转: MySQL 赋予用户权限(grant %-远程和localhost-本地区别)

2019独角兽企业重金招聘Python工程师标准>>> ‍‍相关参考资料&#xff1a;MySQL 赋予用户权限命令的简单格式可概括为&#xff1a;grant 权限 on 数据库对象 to 用户一、grant 普通数据用户&#xff0c;查询、插入、更新、删除 数据库中所有表数据的权利。 grant s…

kafka控制台模拟消费_Kafka 详解

kafka简介Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需…

python在web可以开发吗_Python Web开发

参考原文 WSGI接口 WSGI&#xff08;Web Server Gateway Interface&#xff09;是一个接口&#xff0c;用来屏蔽底部的细节&#xff08;如TCP的建立连接&#xff0c;HTTP原始请求和响应格式等&#xff09;。WSGI接口定义非常简单&#xff0c;只需要Web开发者实现一个函数&#…

更新丨.NET 7 预览版2 中的 ASP.NET Core

点击上方蓝字 关注我们&#xff08;本文阅读时间&#xff1a;6分钟).NET 7 预览版2 现已推出&#xff0c;其中包括对 ASP.NET Core 的许多重大改进。以下是此预览版中新增内容的摘要&#xff1a;• 推断来自服务的 API 控制器操作参数&#xff1b;• SignalR 集线器方法的依赖注…

LoadRunner+Android模所器实现抓包并调试本地服务端

为了测试Android软件的服务端的功能&#xff0c;需要重现某些客户端操作&#xff0c;便于发现功能问题&#xff0c;性能问题。也方便客户端与本机服务端特别是服务端代码进行断点调试。这个时候需要对网络操作进行重现。loadRunner是hp公司开发的压力测试工具。功能比较强大&am…