Spring整合tomcat的WebSocket详细逻辑(图解)

Spring整合tomcat的WebSocket详细逻辑(图解)

主要解决存在的疑问

  1. 为什么存在2种spring整合websocket的方式,一种是使用@ServerEndpoint注解的方式,一种是使用@EnableWebSocket注解的方式,这2种有什么区别和联系?可以共存吗?它们实现的原理是什么?它们的各种配置到底是什么意思?@EnableWebSocket是如何完成配置的?
  2. 在服务端和客户端建立websocket连接的时候,如何做认证?不能让任意的客户端连接到websocket服务端,而且不应该在建立websocket连接之后再认证,而是应该在握手的时候,就去做认证,该如何实现?

在这里插入图片描述

spring、tomcat是如何配合完成websocket

这篇文章觉得不错,原文地址:spring、tomcat是如何配合完成websocket

综述

像IM这一类web系统,需要有机制知道是否有新消息,没有websocket前主要靠轮训。
轮训频率设得过高,有效轮训率低,不仅消耗网络资源,还占用cpu资源;轮训频率设得过低,又会造成消息延时较大。
为此诞生了websocket,消息可以由服务端主动推送到客户端,不仅实时性高,效率也是拉满。
为了尽量减少对现有系统进行改造,websocket是在建立在http的基础之上的,这样不仅可以复用http的端口,服务端及客户端的改造都更小。

websocket协议

在这里插入图片描述

  1. 握手阶段:如上所述websocket是基于http,主要是握手阶段仍使用http协议,当HTTP处理器发现头部带“Upgrade: websocket”,则认为连接是要升级到websocket的协议;如果支持websocket则返回允许升级到websocket的响应。
  2. 通信阶段:客户端接受到服务端允许升级协议的响应后,则认为握手完成,后续都以websocket格式发送报文。

websocke报文格式如下:
在这里插入图片描述
websocket协议本身很简单,各个字段的含义可以参考其他文章,这里不再详述,本文主要分析tomcat如何实现websocket以及spring boot如何基于tomcat集成websocket。

本文基于tomcat NIO模式进行分析。

分析前可以大致梳理一下,有哪些关键点。

  1. tomcat NIO模式下,会为每个请求分配一个线程进行处理,websocket是长连接,这个线程是否会与websocket连接绑定,而一直被同一个websocket占有。
  2. websocket协议升级是在那个点触发的。
  3. tomcat是如何在http的基础之上支持websocket
  4. tomcat的websocket是如何暴露接口给spring去集成的。

tomcat NIO模式下线程模型

在这里插入图片描述
各个线程的初始化详见org.apache.tomcat.util.net.NioEndpoint#startInternal

  1. Acceptor线程主逻辑用于调用accept方法接受请求,生成socket并在Poller中注册
  2. Poller线程主逻辑是调用select方法,获取可读写的socket,并创建SocketProcessor,并丢给Processor线程进行处理。
  3. Processor线程主要逻辑是根据协议解析socket中的数据,并调用servlet容器进行业务处理。

tomcat NIO模式下请求处理流程

在这里插入图片描述
本文不详细解析tomcat的内部实现,http协议本身很简单,没有握手的过程,仅仅只是简单的请求/应答。理论上,只需要 接受请求 -> 解析报文 -> 丢给servlet处理 这几个过程。
tomcat为了实现更丰富的功能抽象出Engine,Host,Context等概念,为了便于理解我们简化一下tomcat模型,我们将CoyoteAdapter到StandardWrapper当成servlet容器内部的行为,将其合并为Servlet Container,其功能就是将request路由到正确的servlet进行处理。
那么请求的处理过程可以简化成

在这里插入图片描述

  1. SocktProcessor主要做协议解析,将socket中的字节流转换成一帧帧HTTP报文
  2. servlet container主要请求路由,跟url信息转发到正确的servlet进行处理
  3. FilterChain这个是servlet规范中的FilterChain,在请求在交给servlet处理前,会经过一系列的Filter进行处理
  4. servlet这个就不解释了。

websocket协议升级涉及的点

  1. websocket与http有点区别,websocket是长连接(虽然http也可以配置 keepalive在实现长连接,但是服务端并不保证客户端一直没有发请求的情况下仍然保持连接),正常情况下只有双方其中一端显式关闭连接,才能结束这个socket。所以SocktProcessor要了解到该socket连接是有升级过协议的;不仅如此因为websocket是基于http,共用端口,所以SocktProcessor也要感知到socket连接是升级过协议,这样完成握手后的请求,要由websocket的协议处理器去做协议解析。
  2. websocket协议本身并没有鉴权等设计,这个需要委托给握手阶段的http报文的处理。为此握手阶段的报文要当成正常的http协议处理,需要走所有已配置的Filter;所以握手阶段的处理一定会走到servlet容器里面,而且spring应该会注册专门的handler去处理升级前的http报文。
  3. servlet处理模型与websocket不一致,所以框架里面应该会有注册websocket的handler的逻辑。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C# 中使用 WebSocket 传输图像,可以通过将图像数据转换为字节数组,并使用 WebSocket 发送字节数组来实现。以下是一个简单的示例代码: ```csharp using System; using System.Drawing; using System.IO; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; public class WebSocketClient { private ClientWebSocket webSocket; public async Task Connect(string url) { webSocket = new ClientWebSocket(); await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); } public async Task SendImage(string imagePath) { if (webSocket.State != WebSocketState.Open) { throw new Exception("WebSocket connection is not open."); } using (var image = new Bitmap(imagePath)) using (var stream = new MemoryStream()) { image.Save(stream, System.Drawing.Imaging.ImageFormat.Png); byte[] imageData = stream.ToArray(); await webSocket.SendAsync( new ArraySegment<byte>(imageData), WebSocketMessageType.Binary, endOfMessage: true, CancellationToken.None); } } public async Task ReceiveImage(string savePath) { if (webSocket.State != WebSocketState.Open) { throw new Exception("WebSocket connection is not open."); } var buffer = new byte[1024]; var receivedData = new MemoryStream(); while (true) { var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); receivedData.Write(buffer, 0, result.Count); if (result.EndOfMessage) { using (var image = Image.FromStream(receivedData)) { image.Save(savePath); } break; } } } public async Task Disconnect() { if (webSocket.State == WebSocketState.Open) { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); } webSocket.Dispose(); } } ``` 使用示例: ```csharp static async Task Main() { var client = new WebSocketClient(); await client.Connect("wss://websocket-server-url"); // 发送图像 await client.SendImage("path_to_image.png"); // 接收图像 await client.ReceiveImage("received_image.png"); await client.Disconnect(); } ``` 请注意,此示例仅用于演示目的,实际应用中可能需要进行错误处理和其他逻辑

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值