对于BeetleX来说编写WebSocket服务是一件非常简单的事情,当你实现一个Web Api应用的同时这些API方法也是WebSocket服务方法。接下来主要讲解如何通过JavaScript调用BeetleXWebSocket服务方法或定义一个适合自己数据格式的WebSocket服务。

引用组件

通过Nuget引用最新版本的BeetleX.FastHttpApi或通过下载源码编译组件

实现服务

由于组件支持基于类方法的方式来制定服务,所以定义一个服务非常简单,以下是一个基于Websockethello world服务:

    [BeetleX.FastHttpApi.Controller]
class Program
{
private static BeetleX.FastHttpApi.HttpApiServer mApiServer; static void Main(string[] args)
{
mApiServer = new BeetleX.FastHttpApi.HttpApiServer();
mApiServer.Debug();
mApiServer.Register(typeof(Program).Assembly);
mApiServer.Open();
Console.Write(mApiServer.BaseServer);
Console.Read();
} public string Hello(string name)
{
return $"{name} {DateTime.Now}";
}
}

JavaScript调用

由于组件定义一个调用规范,针对上面的方法调用有着一定的格式要求,大体的json格式如下:

{
url:''
params:{{"name":"value"},{"name1":"value1"}}
}
  • url

    描述请求的方法路径,针对以上示例对应的路径是'/Hello',组件默认大小写不敏感。

  • params

    用于描述方法对应的参数列表

针对以上示例方法调用json如下:

{
url: '/Hello',
params: { name: 'test' }
}

自己处理数据

组件的服务要求指定的请求格式和对应的响应格式,这样对于一些使用者来说有些限制,如果不希望组件提供的格式而是自己制定数据方式的话可以绑定WebSocket数据接收事件,当事件绑定后组件会把接收的数据直接路由给事件来处理,不会再按原有的方式来解析处理。绑定事件如下:

  mApiServer.WebSocketReceive = (o, e) =>
{
Console.WriteLine(e.Frame.Body);
var freame = e.CreateFrame($"{DateTime.Now}" + e.Frame.Body.ToString());
e.Response(freame);
};

不过这里的处理方式还是以文本为主,只是文本的格式解释和输出格式更多的进行控制。

处理非文本数据

默认情况都以文本的方式来处理数据,实际上websocket是支持二进制流的;如果希望在组件的基础上自己处理二进制流数据需要制定一个数据解析器,解析器的接口规范如下:

    public interface IDataFrameSerializer
{
object FrameDeserialize(DataFrame data, PipeStream stream);//反序列化对象方法 ArraySegment<byte> FrameSerialize(DataFrame packet, object body);//序列化方法 void FrameRecovery(byte[] buffer);//Buffer回收方法 }

组件默认的解析器实现如下:

        public virtual object FrameDeserialize(DataFrame data, PipeStream stream)
{
return stream.ReadString((int)data.Length);
} private System.Collections.Concurrent.ConcurrentQueue<byte[]> mBuffers = new System.Collections.Concurrent.ConcurrentQueue<byte[]>(); public virtual ArraySegment<byte> FrameSerialize(DataFrame data, object body)
{
byte[] result;
if (!mBuffers.TryDequeue(out result))
{
result = new byte[this.Options.MaxBodyLength];
}
string value;
if (body is string)
value = (string)body;
else
value = Newtonsoft.Json.JsonConvert.SerializeObject(body);
int length = Options.Encoding.GetBytes(value, , value.Length, result, );
return new ArraySegment<byte>(result, , length);
} public virtual void FrameRecovery(byte[] buffer)
{
mBuffers.Enqueue(buffer);
}

在制定完成数据解析器后把它设置到FrameSerializer属性上即可

HttpApiServer.FrameSerializer= new CustomFrameSerializer();

连接验证

当通过浏览器访问websocket服务的时候,在连接创建过程存在一个握手通讯包,这个通讯包一般都带有用户的Cookie,通过这个Cookie即可以验证连接的来源,从而确定连接的有效性。组件提供一个WebSocketConnect事件来扩展这个验证机制,事件制定如下:

            mApiServer.WebSocketConnect = (o, e) => {
//e.Request.Header
//e.Request.Cookies
e.Cancel = true;
};

使用者可以根据实际情况的需要判断对应的数据来确定是否取消当前WebSocket连接

基于流解释WebSocket协议

网上有很多WebSocket协议解释代码,之前也写过一份,不过都是针对byte[]进行分析,以下代码是基于Stream的方式来分析协议,通过stream操作起来会更简洁易懂

        internal DataPacketLoadStep Read(PipeStream stream)
{
if (mLoadStep == DataPacketLoadStep.None)
{
if (stream.Length >= )
{
byte value = (byte)stream.ReadByte();
this.FIN = (value & CHECK_B8) > ;
this.RSV1 = (value & CHECK_B7) > ;
this.RSV2 = (value & CHECK_B6) > ;
this.RSV3 = (value & CHECK_B5) > ;
this.Type = (DataPacketType)(byte)(value & 0xF);
value = (byte)stream.ReadByte();
this.IsMask = (value & CHECK_B8) > ;
this.PayloadLen = (byte)(value & 0x7F);
mLoadStep = DataPacketLoadStep.Header;
}
}
if (mLoadStep == DataPacketLoadStep.Header)
{
if (this.PayloadLen == )
{
if (stream.Length >= )
{
Length = stream.ReadUInt64();
mLoadStep = DataPacketLoadStep.Length;
}
}
else if (this.PayloadLen == )
{
if (stream.Length >= )
{
Length = stream.ReadUInt16();
mLoadStep = DataPacketLoadStep.Length;
}
}
else
{
this.Length = this.PayloadLen;
mLoadStep = DataPacketLoadStep.Length;
}
}
if (mLoadStep == DataPacketLoadStep.Length)
{
if (IsMask)
{
if (stream.Length >= )
{
this.MaskKey = new byte[];
stream.Read(this.MaskKey, , );
mLoadStep = DataPacketLoadStep.Mask;
}
}
else
{
mLoadStep = DataPacketLoadStep.Mask;
}
}
if (mLoadStep == DataPacketLoadStep.Mask)
{
if (this.Length > && (ulong)stream.Length >= this.Length)
{
if (this.IsMask)
ReadMask(stream);
Body = this.DataPacketSerializer.FrameDeserialize(this, stream);
mLoadStep = DataPacketLoadStep.Completed;
}
}
return mLoadStep;
}
void IDataResponse.Write(PipeStream stream)
{
try
{
byte[] header = new byte[];
if (FIN)
header[] |= CHECK_B8;
if (RSV1)
header[] |= CHECK_B7;
if (RSV2)
header[] |= CHECK_B6;
if (RSV3)
header[] |= CHECK_B5;
header[] |= (byte)Type;
if (Body != null)
{
ArraySegment<byte> data = this.DataPacketSerializer.FrameSerialize(this, Body);
try
{
if (MaskKey == null || MaskKey.Length != )
this.IsMask = false;
if (this.IsMask)
{
header[] |= CHECK_B8;
int offset = data.Offset;
for (int i = offset; i < data.Count; i++)
{
data.Array[i] = (byte)(data.Array[i] ^ MaskKey[(i - offset) % ]);
}
}
int len = data.Count;
if (len > && len <= UInt16.MaxValue)
{
header[] |= (byte);
stream.Write(header, , );
stream.Write((UInt16)len);
}
else if (len > UInt16.MaxValue)
{
header[] |= (byte);
stream.Write(header, , );
stream.Write((ulong)len);
}
else
{
header[] |= (byte)data.Count;
stream.Write(header, , );
}
if (IsMask)
stream.Write(MaskKey, , );
stream.Write(data.Array, data.Offset, data.Count);
}
finally
{
this.DataPacketSerializer.FrameRecovery(data.Array);
}
}
else
{
stream.Write(header, , );
}
}
finally
{
this.DataPacketSerializer = null;
this.Body = null;
}
}

如果你对这代码有兴趣,最直接的方法是查看BeetleX的代码https://github.com/IKende/FastHttpApi

BeetleX之WebSocket详解的更多相关文章

  1. WebSocket 详解

    WebSocket 出现前 构建网络应用的过程中,我们经常需要与服务器进行持续的通讯以保持双方信息的同步.通常这种持久通讯在不刷新页面的情况下进行,消耗一定的内存资源常驻后台,并且对于用户不可见.在 ...

  2. webSocket详解

    WebSocket 实战http://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/index.html 转自IBMdeveloperWorks ...

  3. spring WebSocket详解

    场景 websocket是Html5新增加特性之一,目的是浏览器与服务端建立全双工的通信方式,解决http请求-响应带来过多的资源消耗,同时对特殊场景应用提供了全新的实现方式,比如聊天.股票交易.游戏 ...

  4. WebSocket 详解教程

    WebSocket 详解教程 概述 WebSocket 是什么? WebSocket 是一种网络通信协议.RFC6455 定义了它的通信标准. WebSocket 是 HTML5 开始提供的一种在单个 ...

  5. tornado WebSocket详解

    1.什么是WebSocketwebsocket和长轮询的区别是客户端和服务器之间是持久连接的双向通信.协议使用ws://URL格式,但它在是在标准HTTP上实现的. 2.tornado的WebSock ...

  6. WebSocket详解(一):初步认识WebSocket技术

    1.什么是Socket?什么是WebSocket? 对于第1次听说WebSocket技术的人来说,两者有什么区别?websocket是仅仅将socket的概念移植到浏览器中的实现吗? 我们知道,在网络 ...

  7. 理论经典:TCP协议的3次握手与4次挥手过程详解

    1.前言 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提供一种面向连接的.可靠的字节流服务. 面向连接意味着两个使用TCP的应用(通常是一个客户和一 ...

  8. 新手入门:史上最全Web端即时通讯技术原理详解

    前言 有关IM(InstantMessaging)聊天应用(如:微信,QQ).消息推送技术(如:现今移动端APP标配的消息推送模块)等即时通讯应用场景下,大多数都是桌面应用程序或者native应用较为 ...

  9. Web端即时通讯技术原理详解

    前言 有关IM(InstantMessaging)聊天应用(如:微信,QQ).消息推送技术(如:现今移动端APP标配的消息推送模块)等即时通讯应用场景下,大多数都是桌面应用程序或者native应用较为 ...

随机推荐

  1. 【莫比乌斯反演】BZOJ1101 [POI2007]zap

    Description 回答T组询问,有多少组gcd(x,y)=d,x<=a, y<=b.T, a, b<=4e5. Solution 显然对于gcd=d的,应该把a/d b/d,然 ...

  2. BZOJ_2743_[HEOI2012]采花_离线+树状数组

    BZOJ_2743_[HEOI2012]采花_离线+树状数组 Description 萧芸斓是Z国的公主,平时的一大爱好是采花.今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花 .花园足够大 ...

  3. BZOJ3252: 攻略 可并堆

    网上有很多人说用dfs序+线段树做...其实stl的堆可以...可并堆可以...很多奇奇怪怪的东西都能做... 可并堆比较好想...也比较好写... 分析: 首先,这是一个网络流做不了的题...数据太 ...

  4. python使用sqlmap API检测SQL注入

    0x00前言: 大家都知道sqlmap是非常强大的sql注入工具,最近发现他有个sqlmap API,上网查了一下.发现这是 sqlmap的微端.(可以叫做sqlmap在线检测sql注入= =) 0x ...

  5. .Net开发者必知的技术类RSS订阅指南

    目录 RSS订阅资源 .Net基金会 MSDN中文版 杂志 微软 Github 系列 微软DevBlog系列 InfoQ中文版系列 如何找到大佬的 Twitter/Youtube/Stackoverf ...

  6. Haskell学习-monad

    原文地址:Haskell学习-monad 什么是Monad Haskell是一门纯函数式的语言,纯函数的优点是安全可靠.函数输出完全取决于输入,不存在任何隐式依赖,它的存在如同数学公式般完美无缺.可是 ...

  7. Redis 实战篇之搭建集群

    Redis 集群简介# Redis Cluster 即 Redis 集群,是 Redis 官方在 3.0 版本推出的一套分布式存储方案.完全去中心化,由多个节点组成,所有节点彼此互联.Redis 客户 ...

  8. ASP.NET Core中使用GraphQL - 最终章 Data Loader

    ASP.NET Core中使用GraphQL - 目录 ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间 ...

  9. 小步快跑的公司可以最简化操作直接通过log4net将日志写入ElasticSearch

     很多小步快跑的公司,开发人员多则3-4个,面对巨大业务压力,日连夜的赶着上线,快速试错,自然就没时间搭建一些基础设施,比如说logCenter,但初期 项目不稳定,bug又多,每次都跑到生产去找日志 ...

  10. DDL(数据定义语言)

    1.Oracle中常见的数据类型分类:(A) 1.number(x,y) 数字类型,x表示最大长度,y表示精度对应java中除char外所有基本数据类型(byte.short.int.long.flo ...