一种仿照Asp.net Mvc思维构建WebSocket服务器的方法
发布日期:2021-05-09 04:10:24 浏览次数:15 分类:博客文章

本文共 2995 字,大约阅读时间需要 9 分钟。

问题场景

Asp.net Mvc提供了DependencyResolver、Routing、Filter、 Modelbinder等webForm所没有新概念,提高Web服务编写的便利性,记得很久之前写的ashx处理程序,由于没有Routing和Modelbinder,代码里写了很多switch case,还有很多参数类型转换,写得满头大汗。现在,开发WebSocket服务端时,同样遇到和ashx差不多的状况:解析数据包,分析Command值,switch(command),然后一个case一个case分支的服务逻辑实现。

优化思路

如果我们在webSocket协议之上提出一种请求和回复的数据包的格式约定,正如http在tcp之上的协议约定一样,那么就可以仿照Asp.net Mvc一样,实现服务端的DependencyResolver、Controller、Filter等类似功能,未来业务功能的开发只要继承Controller即可,轻松地实现业务功能代码和基础通讯代码完全分开。当然这个格式约定可以作很简单化,而不是直接复制Http协议,我们现在约定的格式可以如下:

{"api":"Login","id":2,"body":["name","password"]}
  • 请求和回复的内容都为Json文本;
  • api指明请求到远程端的哪个api方法;
  • id为本数据包的唯一标识符;
  • body为请求的远程端api的参数值,为数组;如果是回复,则为回复的对象的json文本 

客户端请求如上的数据到服务器,服务器就自行调用它里面的Login方法,然后将返回值放到请求json的body字段返回给客户端:

public bool Login(string theName, string thePassword){     return theName == "name" && thePassword == "password";}

 

 

设计之道

Api服务基础类(FastApiService)的设计

上面的Login方法是一个具体的业务Api,其所在的class派生于FastApiService,FastApiService的职责是反射调用其Login成员方法。

关于反射性能,可以对Login方法先生成一个调用的委托,缓存起来供下次调用,可以参考asp.net Mvc的ActionMethodDispatcher:

FastApiService的职责接口如下:

/// /// 定义Api服务的执行/// public interface IFastApiService : IDisposable{     ///      /// 执行Api行为     ///                    /// Api行为上下文           void Execute(ActionContext actionContext);}

 

Routing的设计

这里我们偷工减料了,不作那么强大,分析请求数据包的api键的字符串值,查找哪个FastApiService定义了相关的成员方法,从而New出这个FastApiService实例,再调用Execute(ActionContext actionContext);

 

DependencyResolver的设计

Asp.netMvc+Autofac管理EF的Context对象非常方便,这得利于Asp.netMvc提供了DependencyResolver,可以把Controller的创建给IOC组件来管理,DependencyResolver接口很简单,传入对象类型,返回对象实例,中间过程由IOC来处理。

查找哪个FastApiService定义了相关的成员方法,从而New出这个FastApiService实例

这里获取FastApiService的实例,改为DependencyResolver来获取

 

各部件执行流程

Filter哪里去了

Filter实际是附属的一种东西,在FastApiService的Execute前和后各执行各种Filter就可以了,不管是全局的Filter,还是打特性的,终究都是Filter,约定好他们的执行顺序就OK!有了Filter,妈妈再也不担心别人还未登录就请求我的其它Api服务了。

 

 

成果展示

服务器c#代码片断

///     /// Cpu性能检测控制服务    ///        public class CpuCounterService : FastApiService    {        ///         /// 获取版本号        ///         /// 
[Api] [LogFilter("获取版本号")] public string GetVersion() { return this.GetType().Assembly.GetName().Version.ToString(); } /// /// 订阅/取消Cpu变化通知 /// ///
[Api] [LogFilter("订阅/取消Cpu变化通知")] public bool SubscribeCpuChangeNotify(bool subscribe) { this.CurrentContext.Session.TagData.Set("NotifyFlag", subscribe); return true; } }

 

客户端js代码片断

document.title = '正在连接到服务器 ..';        var ws = new fastWebSocket('ws://localhost:8282/');        // 注册api        ws.bindApi("CpuTimeChanged", function (data) {            lineChart.addData(data);        });        ws.onclose = function (e) {            document.title = '连接已断开:' + e.code + '' + e.reason;        };        ws.onopen = function (e) {            ws.invkeApi('getVersion', [], function (version) {                document.title = '服务器版本号:' + version;            }, function (ex) {                alert('异常:' + ex);            });        };

 

 

栗子下载

 

上一篇:那些不好的Socket服务器设计
下一篇:js的alert和confirm美化

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月11日 09时46分38秒