.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

Binary1

消息采用二进制格式。

Close2

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

Text0

该消息是明文形式。

如果看过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是允许双向检测心跳的,既然检测到心跳失败,那么怎么就不留个通知的方法?

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

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

相关文章

Linux C++ 023-类模板

Linux C 023-类模板 本节关键字&#xff1a;Linux、C、类模板 相关库函数&#xff1a;getCapacity、getSize 类模板语法 类模板的作用&#xff1a;建立一个通用的类&#xff0c;类中的成员 数据类型可以不具体制定&#xff0c; 用一个虚拟的类型代表语法&#xff1a; templa…

stable diffusion的从安装到使用

stable-diffusion&#xff0c;一个免费开源的文生图软件&#xff0c;文章主要讲怎么从源码开始安装&#xff0c;以及使用的方式 git地址&#xff1a;https://github.com/AUTOMATIC1111/stable-diffusion-webui 本人电脑环境win10&#xff0c;软件pycharm&#xff0c;需要提前…

Java关键字保留字(共53个)

保留字&#xff08;2个&#xff09; 保留字&#xff08;Reserve Word&#xff09;&#xff1a;即它们在Java现有版本中没有特殊含义&#xff0c;以后版本可能会作为有特殊含义的词&#xff0c;或者该词虽然在Java中没有特殊含义&#xff0c;以后版本也不打算使用&#xff0c;但…

jeecg-boot 3.6使用微服务启动详细配置

1&#xff1a;运行sql文件 2&#xff1a;配置host 路径如下 127.0.0.1 jeecg-boot-redis 127.0.0.1 jeecg-boot-mysql 127.0.0.1 jeecg-boot-nacos 127.0.0.1 jeecg-boot-gateway 127.0.0.1 jeecg-boot-system 127.0.0.1 jeecg-boot-xxljob 127.0.0.1 jeecg-boot-rabbitmq 3…

LeetCode第十五题:三数之和【15/1000 python】

&#x1f464;作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任大厂数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python 作者专栏每日更新&#xff1a; LeetCode解锁1000题: 打怪升级之旅 LeetCode解锁1000题: 打怪升级之旅htt…

gitea详细介绍

Gitea 是一个轻量级、易于安装的 Git 服务&#xff0c;提供了类似于 GitHub 的功能&#xff0c;如代码托管、问题追踪、团队合作等。它使用 Go 语言开发&#xff0c;可以在自己的服务器上进行部署&#xff0c;从而实现自托管的 Git 服务。Gitea 具有用户友好的界面&#xff0c;…

数据研发八股文(1)

数据库 hadoop与spark结构 Hadoop和Spark在结构上都包含了多个核心组件&#xff0c;但它们的具体实现和用途有所不同。 Hadoop的结构主要包括HDFS&#xff08;Hadoop Distributed File System&#xff09;和MapReduce。HDFS是一个分布式文件系统&#xff0c;用于存储海量的数据…

【管理咨询宝藏52】AA银行企业文化策略分析报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏52】AA银行企业文化策略分析报告 【格式】PPT版本&#xff0c;可编辑 【关键词】战略规划、商业分析、管理咨询 【强烈推荐】这是一套市面上非常…

npm包安装与管理:深入解析命令行工具的全方位操作指南,涵盖脚本执行与包发布流程

npm&#xff0c;全称为Node Package Manager&#xff0c;是专为JavaScript生态系统设计的软件包管理系统&#xff0c;尤其与Node.js平台紧密关联。作为Node.js的默认包管理工具&#xff0c;npm为开发者提供了便捷的方式来安装、共享、分发和管理代码模块。 npm作为JavaScript世…

Unity MySql安装部署与Unity连接 上篇

1.前言 最近项目用到MySql&#xff0c;记录一下安装部署过程。 数据量过大或者需要管理用户数据的时候用mysql的话数据结构比较清晰明了&#xff0c;便于管理。 2.安装MySql Unity版本&#xff1a;2019.4.16 MySql版本&#xff1a;8.2.0 下载地址&#xff1a;MySql 下载…

【数据结构】利用顺序表实现通讯录

文章目录 前言通讯录要求利用顺序表的现有功能代码呈现 前言 这篇文章实现的通讯录利用了笔者上一篇写的有关顺序表的应用 https://blog.csdn.net/2301_77954967/article/details/137360029?spm1001.2014.3001.5502&#xff0c;需要用的朋友自行复制 通讯录要求 1)至少能够…

PostgreSQL入门到实战-第七弹

PostgreSQL入门到实战 PostgreSQL查询语句(四)官网地址PostgreSQL概述PostgreSQL中DISTINCT 语句介绍PostgreSQL中DISTINCT 语句实操更新计划 PostgreSQL查询语句(四) 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https:…

【测试篇】Selenium + Java环境搭建

文章目录 Selenium Java环境搭建配置系统环境变量PATH验证环境是否搭建成功常见问题&解决办法 Selenium Java环境搭建 Java版本最低要求为8&#xff0c;这里默认大家都下载好了Java。&#x1f606; 下载chrome浏览器&#xff08;点我下载&#xff09; 观察chrome版本。…

设备监控公有云

在数字化浪潮的推动下&#xff0c;越来越多的企业开始关注设备监控公有云这一重要领域。设备监控公有云通过云计算技术&#xff0c;实现对设备的远程监控、管理和维护&#xff0c;大大提高了企业的运营效率和管理水平。HiWoo Cloud平台作为领先的设备监控公有云解决方案提供商&…

【数据库】PostgreSQL源码编译安装方式与简单配置(v16.2)

PostgreSQL源码编译安装方式与简单配置&#xff08;v16.2&#xff09; 一、PostgreSQL安装基本介绍1.1 几种PostgreSQL的安装方式1.2 删除原有的PostgreSQL1.3 编译安装过程简介 二、源码编译安装方式详情2.1 下载源代码2.2 编译安装运行 configure执行 make执行 make install …

力扣207.课程表

你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课程 ai 则 必须 先学习课程 bi 。 例如…

SSM党员管理系统

一、系统介绍 党员管理系统: 可以方便管理人员对党员管理系统的管理&#xff0c;提高信息管理工作效率及查询效率&#xff0c;有利于更好的为用户提供服务。 主要的模块包括&#xff1a; 1、后台功能&#xff1a; 管理员角色&#xff1a;首页、个人中心&#xff0c;党员管理…

前端开发语言概览:从HTML、CSS到JavaScript

随着互联网的发展&#xff0c;前端开发领域涌现出了许多不同的编程语言和技术&#xff0c;用于构建各种类型的网页和应用程序。本文将介绍几种主流的前端开发语言&#xff0c;包括 HTML、CSS 和 JavaScript&#xff0c;并简要讨论它们在前端开发中的作用和特点。 1. HTML&…

Naiveui将message挂载到axios拦截器

最近在做项目&#xff0c;需要将后端的请求结果打印出来 但是想着&#xff0c;要是这样一个一个手动引入naiveui的msg&#xff0c;那不得累死 于是灵机一动&#xff0c;想着既然所有接口要通过拦截器&#xff0c;为什么不将msg写在拦截器呢 一、定义一个消息挂载文件 // The…

【java面试题-Redis篇-2024】

##java面试题大全 详细面试题-持续更新中-点击跳转 点赞、收藏、加关注 java基础面试题 ##java面试题大全1、什么是 Redis2、Redis 的数据结构类型3、Redis 为什么快4、什么是跳跃表5、什么是 I/O 多路复用6、什么是缓存击穿、缓存穿透、缓存雪崩7、什么是布隆过滤器8、热…