.net 实现的 Webscoket 对象的一些细节和疑问

这篇具有很好参考价值的文章主要介绍了.net 实现的 Webscoket 对象的一些细节和疑问。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

这两天服务器和客户端进行了webscoket的联调,在和C#的webscoket实现联调的过程中,发现一些有趣的事情。

在我自己C++的实现中,webscoket对上层应用而言是完全透明的,webscoket 只是一个传输协议,用户对此不需要有任何关注,一切都自动进行,包括连接,握手升级,帧切割,帧拼合,控制帧管理,心跳这些,对外完全透明。

微软.net 中的webscoket实现就显得非常难以理解,握手需要用户主动去调用,这个从HTTP升级的操作还能理解:

public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
}

但是到了发送和接收阶段,先看看这两个函数的示例代码:

private async Task Echo(HttpContext context, WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    while (!result.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

        result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    }
    await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

首先,接受函数需要指定一个缓冲区,由缓冲区来接受webscoket上传输的数据,这个属于常规操作,继续看发送函数, 你会发现在参数中,有两个参数:MessageType 和 EndOfMessage,看看微软怎么描述的:

参数

buffer

ArraySegment<Byte>

要通过连接发送的缓冲区。

messageType

WebSocketMessageTyp

指示应用程序是发送二进制消息还是发送文本消息。

endOfMessage

Boolean

指示“缓冲区”中的数据是否是消息的最后一部分。

cancellationToken

CancellationToken

传播有关应取消操作的通知的标记。

关于 WebSocketMessageType

Binary 1

消息采用二进制格式。

Close 2

因为收到关闭的消息,接受已完成。

Text 0

该消息是明文形式。

如果看过RFC6455是不是感到有些似曾相识?微软做了一次封装,但是也混淆了一些概念,把本应由WebSocket 内部处理的逻辑暴露给外部的调用。我暂时还没想明白微软为啥要这么设计,在我理想的模型里面,包括数据切割分段发送这些事情,不应该是WebScoket的实现自动处理吗?特别是endOfMessage这个布尔值,更让人费解。这个参数对应的opcode 中的 Fin 位?文档中没有说明。客户端在写代码的时候,暂时没有改变这个值,直接设置的 true。

而且关于subprotocol,微软也没有给出一个明确的框架,只是给出一个string,作为subprotocol的标志。我猜想,微软把这个暴露出来,也许就是为subprotocol留下可操作的空间,但文档里面没有明确的说明。在我看来,一个好的设计,应该保持某种程度的兼容性,比如和Scoket的SendAsync保持某种程度的一致。subprotocol应该设计成WebSocket的一个子对象,微软提供一个标准的调用接口,将所有协议细节都放在子协议接口之中,而不是像这样暴露出来。

另外还有一个问题,就是WebScoket的ping / pong心跳检测机制。我翻遍了微软提供的文档,只是这么提到ping和pong:

处理客户端连接断开

当客户端由于失去连接而断开连接时,不会自动向服务器发送通知。 服务器只有在客户端发送通知时才会收到断开连接消息,而此操作无法在失去 Internet 连接的情况下进行。 如果想要在发生此情况时采取某个操作,在特定时间范围内未收到来自客户端的任何消息后设置超时。

如果客户端并非总是发送消息,并且你不希望仅由于连接进入空闲状态就设置超时,则让客户端使用一个计时器并每隔 X 秒发送一条 ping 消息。 在服务器上,如果某条消息在上一条消息发出后的 2*X 秒内尚未到达,则终止连接并报告客户端已断开连接。 等待两次预测的时间间隔,以便为可能延迟 ping 消息的网络延迟提供额外的时间。

备注

如果 KeepAliveInterval 选项(默认为 30 秒 (TimeSpan.FromSeconds(30)))大于零,则内部 ManagedWebSocket 将隐式处理 Ping/Pong 帧,以使连接保持活动状态。

可以看到,微软是实现了心跳检测的,KeepAliveInterval那个选项我经过测试,确定为心跳包的间隔时间,经过测试,发现微软的Ping控制帧是没有附带Payload的,客户端发送到服务器,包括掩码一共6个字节,服务器返回Pong给客户端是两个字节,如果服务器发送附带Payload的Ping帧,微软的WebScoket可以原封不动的把Payload通过Pong帧给服务器返回。

测试中,发现了一个比较棘手的问题,即客户端虽然会发送Ping帧给服务器,但如果服务器不返回任何Pong帧,微软的WebSocket不会有任何动作,不会抛异常,虽然RFC6455没有明确规定心跳帧无法检测的情况该如何处理,但好歹抛一个异常或则事件通知我们?而且关于这一点,微软倒是明确说了:在服务器上,如果某条消息在上一条消息发出后的 2*X 秒内尚未到达,则终止连接并报告客户端已断开连接,那客户端呢?RFC6455是允许双向检测心跳的,既然检测到心跳失败,那么怎么就不留个通知的方法?文章来源地址https://www.toymoban.com/news/detail-849825.html

到了这里,关于.net 实现的 Webscoket 对象的一些细节和疑问的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • .net core 创建WebAPI以及使用EF DBFirst框架使用方法与疑问解答(.net 6)

    EF语法包: 生成实体模型: 修改实体模型: 把生成的实体和上下文都输出到某个文件夹命令 增加JSON格式脚手架: 若想增加某个版本json脚手架,需要加入后缀如: 问题与解决方案: 1、问题: Your startup project \\\'XXX\\\' doesn\\\'t reference Microsoft.EntityFrameworkCore.Design. This package is requi

    2024年02月16日
    浏览(40)
  • 深入探讨 C# 和 .NET 中 async/await 的历史、背后的设计决策和实现细节

    对 async/await 的支持已经存在了十多年。它的出现,改变了为 .NET 编写可伸缩代码的方式,你在不了解幕后的情况下也可以非常普遍地使用该功能。 从如下所示的同步方法开始(此方法是“ 同步的 ”,因为在整个操作完成并将控制权返回给调用方之前,调用方将无法执行任何

    2024年02月08日
    浏览(30)
  • 一些知识点小细节

    当遇到的问题有关逆序输出,可以转换一下思想,就是使用for循环的时候,i的初始化是从数组或者是字符串的最后一个,然后注意设置循环结束的条件,最重要的是不要忘记i--;而不是I++;  注意:当要逆序输出的是字符串类型,可以使用StringBuilder类,因为StringBuilder类有 r

    2024年04月13日
    浏览(32)
  • 【经验】STM32的一些细节

    这两天 碰到的奇葩问题是 STM32定时器同步的问题。 我的设计本意是:使用定时器T3以100us的周期来定时发送命令给 FPGA。由于编码器出结果的最长时间为51us。因此,希望PWM中断要滞后于T3 约60us 。 调试过程:分别在T3和PWM中断中置IO1, IO2. 观察波形,发现IO1和IO2的周期都对,但

    2024年02月20日
    浏览(29)
  • 线性代数的一些小细节

    1 .矩阵乘法满足结合律,但不满足交换律。 如下图中,UWQ三个矩阵,(UW)Q 和U(WQ)的2种结合,证明矩阵乘法满足结合律。 AB 和BA的表达式,如下图中,相同的条件是对应的8项都相同(两个对称矩阵必然满足条件),但是实际上,矩阵展开后的x和y位置是是转置的,只有对角线上

    2024年02月16日
    浏览(25)
  • [react] useState的一些小细节

    因为setState修改是异步的,加上会触发函数重新渲染, 如果代码长这样  一秒再修改,然后重新触发setTImeout, 然后再触发,重复触发循环 如果这样呢 还是会,因为你执行又会重新渲染  为什么修改多次还是跟不上呢? 因为是异步修改 ,所以会出现问题,怎么办?用传函数的形式解决 C

    2024年04月13日
    浏览(30)
  • 利用Echarts画3D线框图一些细节

    1.使用的类型 3D折线图,可以去官网上下载示例改一下就行 2..数据构成 数据实际上是三维点的集合,所以我的点是从后台接口传过来的,而且我的z和xy没有函数关系,所以不能像示例那样写function,但是看示例的代码,其实就是一堆点的列表 3.连线方式 按照点的顺序进行连线

    2024年02月12日
    浏览(27)
  • VisualSVN Server 使用过程中一些细节

    VisualSVN Server 是一款免费的版本管理工具,具有简便易用的特点。本文就使用VisualSVN Server 中的一些常见的细节问题略作称述。 本文使用的相关安装环境如下所示: VisualSVN Server 5.1.3 windows 2012server 这里使用的VisualSVN下载路径:https://www.visualsvn.com/server/download/ ,选择对应的64位

    2024年02月06日
    浏览(39)
  • react中使用路由起手式,一些思路和细节。

    我们选择使用react-router实现路由效果 下载后需要对Route进行引入,是个内置的组件。该组件是有两个属性一个是path,一个是component,path是组件对应的路由,component是对应的组件 App 的最外侧包裹了一个 BrowserRouter 或 HashRouter 2. 导航的a标签改为Link标签 Link to=\\\"/xxx\\\"Demo.Link 3. 导航

    2024年02月13日
    浏览(26)
  • 【手撕Spring源码】一些值得注意的Spring底层细节

    FactoryBean是一个Factory对象,用于生成其他bean示例。当一个bean实现FactoryBean接口后,Spring容器调用其getObject方法返回该工厂所生成的bean,而不是该FactoryBean本身。但是工厂生成的产品对象只能说是一部分受到Spring容器的管理,我们来看看下面的例子: 我们把一个Bean1通过工厂Bean的

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包