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;忽略…

理解云计算备份与灾难恢复

如今&#xff0c;云计算和用户生态系统有一个新的转变&#xff0c;IT的转化创造了一个独特的“数据需求”环境。这意味着有放在这些工作负荷的详细的信息工作和更大的恢复需求。IT管理人员负责管理他们的备份系统&#xff0c;并必须准备好他们的灾难恢复策略。适当的恢复方法是…

SQL Server CLR 集成简介

公共语言运行库 (CLR) 是 Microsoft .NET Framework 的核心,为所有 .NET Framework 代码提供执行环境。 在 CLR 中运行的代码称为托管代码。 CLR 提供执行程序所需的各种函数和服务,包括实时 (JIT) 编译、分配和管理内存、强制类型安全性、异常处理、线程管理和安全性。 …

安卓 收起软件盘

public static void collapseSoftInputMethod(Context context, View v) {if (v ! null) {InputMethodManager imm (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);imm.hideSoftInputFromWindow(v.getWindowToken(), 0);}}传入上下文&#xff…

IOS学习笔记之十七 (NSDate、NSDateFormatter、NSCalendar、NSDateComponents、NSTimer)

1、NSDate 时间与日期的初始化,主要有 [NSDate date]; 2、NSDateFormatter 主要用于NSString和Date之间相互转化//NSDate转NSStringstringFromDate:dt//NSString转NSDatedateFromString 3、NSCalendar和NSDateComponents Date打印出具体时间的年月日和把年月日转化为Date4、N…

android studio adil位置,在Android Studio 中正确使用adil ”绝对经典“

今天调用远程服务中遇到了一个问题&#xff0c;哎&#xff0c;调了2个小时&#xff0c;后来终于解决&#xff0c;总结来看还是对新的Android Studio 不够熟悉。那么。。。。就可以睡觉啦&#xff01;&#xff01;&#xff01;在Android Studio中使用进程通信机制adil时一定要注…

[第二篇]如何在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的丰富知识…

CLR触发器

using System; using System.Data; using System.Data.SqlClient; using Microsoft.SqlServer.Server;public partial class Triggers {// 为目标输入现有表或视图并取消对属性行的注释[Microsoft.SqlServer.Server.SqlTrigger (Name"Trigger1", Target"Unit_E&…

IOS学习笔记十八(copy、mutableCopy、NSCopying、NSMutableCopy、深复制、浅复制)

1、 copy、mutableCopy方法 copy方法返回对象的不可修改的副本 mutableCopy方法返回的对象可修改的副本 1)、测试demo int main(int argc, char * argv[]) {autoreleasepool {NSMutableString *book [NSMutableString stringWithString:"chenyu"];NSMutableStrin…

编程心得03

新的一年2016&#xff0c;再接再厉&#xff01; 转载于:https://www.cnblogs.com/Anzhongliu/p/6091848.html

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

随着第三批光伏领跑者申报标准的出台&#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;觉得这本书不好有这么几…

IOS学习笔记十九NSArray和NSMutableArray

1、NSArray NSArray不可变集合&#xff0c;不能添加新元素和删除已有元素和替换元素 2、demo Dog.h #import <Foundation/Foundation.h> #ifndef Dog_h #define Dog_hinterface Dog : NSObject property (nonatomic, strong) NSString *name; property (nonatomic, as…

centOS安装Mysql指南

centOS安装Mysql指南 说明&#xff1a;使用操作系统centOS6.4 32位系统&#xff1b;mysql:mysql-5.7.10-linux-glibc2.5-i686.tar.gz; 一、准备 下载mysql wget http://mirrors.sohu.com/mysql/mysql:mysql-5.7.10-linux-glibc2.5-i686.tar.gz 注&#xff1a;所有shell命令都以…

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;字典是变成有顺序…