上海缘魁网站建设,app定制开发报价,商品关键词怎么优化,网站口碑营销这两天服务器和客户端进行了webscoket的联调#xff0c;在和C#的webscoket实现联调的过程中#xff0c;发现一些有趣的事情。
在我自己C的实现中#xff0c;webscoket对上层应用而言是完全透明的#xff0c;webscoket 只是一个传输协议#xff0c;用户对此不需要有任何关…这两天服务器和客户端进行了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 ArraySegmentbyte(buffer), CancellationToken.None);while (!result.CloseStatus.HasValue){await webSocket.SendAsync(new ArraySegmentbyte(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);result await webSocket.ReceiveAsync(new ArraySegmentbyte(buffer), CancellationToken.None);}await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
首先接受函数需要指定一个缓冲区由缓冲区来接受webscoket上传输的数据这个属于常规操作继续看发送函数 你会发现在参数中有两个参数MessageType 和 EndOfMessage看看微软怎么描述的 参数 buffer ArraySegmentByte 要通过连接发送的缓冲区。 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是允许双向检测心跳的既然检测到心跳失败那么怎么就不留个通知的方法