【笔记】JavaWeb - Tomcat - WebSocket

在这里插入图片描述

WebSocket

WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道,比如说: 服务器可以在任意时刻发送消息给浏览器。(⚠️这就很厉害了!因为以前的HTTP协议中,服务器都是无法主动发送信息的!!因为HTTP协议是一个请求-响应协议,请求必须先由浏览器发送给服务器,服务器才有机会作出响应把数据发送给浏览器)

在这里插入图片描述

传统HTTP协议的问题

如果服务器无法主动发送请求,这样一来,要在浏览器中搞一个实时聊天,或者在线多人游戏就没法实现。

如果硬要实现:

解决方法问题
借助Flash这个(略带遗憾的)插件浏览器中的flash被乔布斯爷爷干没了
在浏览器中用js进行定时器轮询1. 实时性不够;
2. 大量的请求将大大增加服务器压力
Comet
Comet本质上也是轮询,但是在没有消息的情况下,服务器先拖一段时间,等到有消息了再回复。
这个机制暂时地解决了实时性问题,但是带来了新问题:
1. 服务器需要开启大量的线程数: 以多线程模式运行的服务器会让大部分线程大部分时间都处于挂起状态,极大地浪费服务器资源。
2. 连接不可控: 一个HTTP连接在长时间没有数据传输的情况下,链路上的任何一个网关都可能关闭这个连接,而网关是我们不可控的,这就要求Comet连接必须定期发送一些ping数据表示连接“正常工作”

WebSocket

综上几种机制都是治标不治本,所以HTML5推出了WebSocket标准,让浏览器和服务器之间可以建立无限制的全双工通信,任何一方都可以主动发送消息给对方。

WebSocket不是全新的协议,而是利用了HTTP协议来建立连接。下图展示了WebSocket是如何建立连接的:

在这里插入图片描述

在这里插入图片描述

  1. 首先,WebSocket连接还是由浏览器发起,但是请求协议、请求头有所不同

    1. 请求协议由http变为了ws。(💡但是协议的封装、解析还是走的http那一套)
    2. 请求头中有键值对 Connection: Upgrade Upgrade: websocket

    💡 提示

    还有一些特殊的请求头标识:

    1. Sec-WebSocket-Key 用于标识这个连接是一个BASE64编码的密文,要求服务端响应一个对应加密的Sec-WebSocket-Accept头信息作为应答
    2. Sec-WebSocket-Accept 服务端与客户端一致的密钥计算出来的信息
    3. Sec-WebSocket-Version 指定了WebSocket的协议版本
  2. 服务器响应:

    1. 响应的状态不再是200,而是101
    2. 响应头有键值对: Connection: upgradeUpgrade: websocket

Tomcat 的 WebSocket

Tomcat的7.0.5版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356),而在7.0.5版本之前(7.0.2之后)则采用自定义API,即WebSocketServlet实现。

EndPoint

接口设计

Java WebSocket应用由一系列的WebSocketEndpoint组成。Endpoint是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口,就像Servlet与http请求一样。

我们可以通过两种方式定义Endpoint:

  1. 第一种是编程式,即继承类 javax.websocket.Endpoint 并实现其方法
  2. 第二种是注解式,即定义一个POJO,并添加 @ServerEndpoint 相关注解

生命周期

(无论是编程式定义、还是注解式定义)都要使用Endpoint接口。Endpoint接口实例在WebSocket握手时创建(open),并在客户端与服务端链接过程中有效,最后在链接关闭时结束(close)。

在Endpoint接口中明确定义了与其生命周期相关的方法,规范实现者确保生命周期的各个阶段调用实例的相关方法。声明周期方法如下:

方法含义描述注解
onOpen当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法@OnOpen
onClose当会话关闭时调用@OnClose
onError当连接过程中异常时调用@OnError
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package javax.websocket;

public abstract class Endpoint {

    /**
    💡 当会话(session)开始时触发该事件
     * Event that is triggered when a new session starts.
     *
     * @param session   The new session.
     * @param config    The configuration with which the Endpoint was
     *                  configured.
     */
    public abstract void onOpen(Session session, EndpointConfig config);

    /**
    💡 当会话(session)关闭时触发该事件
     * Event that is triggered when a session has closed.
     *
     * @param session       The session
     * @param closeReason   Why the session was closed
     */
    public void onClose(Session session, CloseReason closeReason) {
        // NO-OP by default
    }

    /**
    💡 当一个协议错误发生时触发该事件
     * Event that is triggered when a protocol error occurs.
     *
     * @param session   The session.
     * @param throwable The exception.
     */
    public void onError(Session session, Throwable throwable) {
        // NO-OP by default
    }
}

MessageHandler

接收消息

通过为Session添加MessageHandler消息处理器来接收消息:

  • 当采用注解方式定义EndPoint时,我们可以通过@OnMessage注解指定接收消息的方法

发送消息

发送消息时则是由RemoteEndPoint完成,其实例由Session维护,根据使用情况,我们可以:

  • 通过 Session.getBasicRemote 获得同步消息发送的实例,然后调用其 sendXXX() 方法发送消息
  • 通过 Session.getAsyncRemote 获取异步消息发送的实例

Tomcat处理WebSocket请求流程 ⭐️

从原理上,看Tomcat是如何处理WebSocket请求的

在这里插入图片描述
Tomcat用ProtocolHandler组件屏蔽应用层协议的差异,ProtocolHandler两个关键组件:Endpoint和Processor。

💡 提示

这里的Endpoint跟WebSocket中的Endpoint完全是两回事,连接器中的Endpoint组件用来处理I/O通信。WebSocket本质是个应用层协议,不能用HttpProcessor处理WebSocket请求,而要用专门Processor,在Tomcat就是UpgradeProcessor。

因为Tomcat是将HTTP协议升级成WebSocket协议的,因为WebSocket是通过HTTP协议握手的,当WebSocket握手请求到来时,HttpProtocolHandler首先接收到这个请求,在处理这个HTTP请求时,Tomcat通过一个特殊的Filter判断该当前HTTP请求是否是一个WebSocket Upgrade请求(即包含Upgrade: websocket的HTTP头信息),如果是,则在HTTP响应里添加WebSocket相关的响应头信息,并进行协议升级。

就是用UpgradeProtocolHandler替换当前的HttpProtocolHandler,相应的,把当前Socket的Processor替换成UpgradeProcessor,同时Tomcat会创建WebSocket Session实例和Endpoint实例,并跟当前的WebSocket连接一一对应起来。这个WebSocket连接不会立即关闭,并且在请求处理中,不再使用原有的HttpProcessor,而是用专门的UpgradeProcessor,UpgradeProcessor最终会调用相应的Endpoint实例来处理请求

在这里插入图片描述

Tomcat对WebSocket请求的处理没有经过Servlet容器,而是通过UpgradeProcessor组件直接把请求发到ServerEndpoint实例,并且Tomcat的WebSocket实现不需要关注具体I/O模型的细节,从而实现了与具体I/O方式的解耦。

案例: 聊天室(浏览器、WebSocket)

todo https://www.bilibili.com/video/BV1dJ411N7Um?p=53

# 实现流程(时序图)

在这里插入图片描述

# 消息格式
客户端 --> 服务端: {"fromName": "fuu", "toName": "boo", "content": "约?"}

服务端 --> 客户端:
1. 如果type为user,则说明返回的是用户列表:
	{"type": "user", "toName": "", "fromName": "","data": "fuu,boo,caa"}
2. 如果type为message,则说明返回的是消息内容:
 	{"type": "message", "toName": "boo", "fromName": "fuu","data": "约!"}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骆言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值