Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)

简介: Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)

什么是 WebSocket

客户端 A 和客户端 B 的消息传播需要借助服务器的中转 (原因是内网不能给另一个局域网的内网直接联通, 需要借助服务器的外网做 “中介”) (NAT 地址转换)

Http 协议 不支持实时通讯 (或者说不支持服务器主动推送数据给客户端)

TCP 本身是具有服务器推送数据这样的功能的, 但是 Http 把它搞丢了 (发明 Http 的大佬们当年所处的时候, 网页就是用来看看新闻, 也没想到后辈能把网页玩出花来)

于是有了 WebSocket 协议.(基于传输层的 TCP 实现) (和 Http 一样, 都是 应用层协议 )

Tomcat 和 Spring 都提供了 WebSocket API, 方便我们通过 WebSocket 协议实现服务器消息推送


Socket 和 WebSocket

Socket 是 OS (或者说传输层) 提供的一组用于网络编程的 API

WebSocket 是一个应用层协议

二者牛马不相及 (没关系)

硬要说有关系, 那就是 WebSocket 的内部实现用到了 Socket

帧格式 (Base Framing Protocol)

FIN : 表示本帧是否是消息的最后一帧, 是1否0

opcode : 解释 payload data 的类型

MASK : 表示 Payload data 是否经过掩码处理

Payload len: 有效载荷长度 ( [0, 125] – 只使用 Payload len空间, [126] – 表示使用 Payload len 和 Extension Payload len 空间, >[127, …] – 使用 Payload len 和 Extension Payload len 和 Extension payLoad len continue 空间) (即不同数值用不同空间来表示载荷的长度)

masking-key : 掩码内容, 当 MASK = 1 时被启用

payload data : 载荷的具体内容

WebSocket 握手过程

握手之前, 客户端和浏览器同为 Http 协议

握手开始, 客户端向服务器发起 Http 请求, 根据请求头里包含的信息, 请求服务器进行协议切换

( Connection: upgrade – 能不能换个协议啊)

( Upgrade: WebSocket – 换成 WebSocket 协议行不行啊)

服务器收到浏览器的请求, 并返回响应, 并切换协议格式

( Connection: upgrade – 能换协议啊)

( Upgrade: WebSocket – 换成 WebSocket 协议 ok 啊)

返回的协议内, 状态码为 101, 明确告诉客户端, 我已经切换协议了, 我们之后用 WebSocket 协议进行通信

WebSocket 和 Http 协议是什么关系?

没关系

Http 就是 工具人

客户端和服务器之间通过 Http 协议建立连接, 连接建立完成之后, 就再也用不到 Http 协议了

WebSocket 在 Spring 中的使用

前端

// 创建 websocket 实例, 或者说进行与服务器建立 WebSocket 连接
// let websocket = new WebSocket("ws://127.0.0.1:8080/WebSocketMessage"); // 操作本地
// let websocket = new WebSocket("ws://152.136.56.110:9090/WebSocketMessage"); //操作远程服务器
let websocket = new WebSocket("ws://" + location.host + "/WebSocketMessage"); //统一格式

websocket.onopen = function() {
  // 该方法在连接建立完成后, 被自动调用
    console.log("websocket 连接成功!");
}

websocket.onmessage = function(e) {
  // 该方法在收到消息时, 被自动调用
    console.log("websocket 收到消息! " + e.data);
    // 处理消息响应
    handleMessage(e.data);  
}

websocket.onclose = function() {
  // 该方法在连接关闭后, 被自动调用
    console.log("websocket 连接关闭!");
}

websocket.onerror = function() {
  // 该方法在连接异常时, 被自动调用
    console.log("websocket 连接异常!");
}

后端

导入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

首先是 创建 Handler 对象, 让其 继承 TextWebSocketHandler

然后将 Handler 注册到 实现 WebSocketConfigurer 接口 的 注册类中

对于 Handler 对象, 有四个常用的方法

afterConnectionEstablished : 该方法在 WebSocket 连接建立 之后, 被自动调用

handlerTextMessage : 该方法在 WebSocket 收到消息 的时候, 被自动调用

handlerTransportError : 该方法在连接 出现异常 的时候, 被自动调用

afterConnectionClosed : 该方法在连接 正常关闭 后, 被自动调用

@Component
public class TestWebSocketAPI extends TextWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 该方法会在 websocket 连接建立之后, 被自动调用
        System.out.println("Test 连接成功!");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 该方法会在 websocket 收到消息的时候, 被自动调用
        System.out.println("Test 收到消息!" + message.toString());
    
    // 这里可以直接用, 是因为在注册器中, 已经把 HttpSession 中的内容给放到 WebSocketSession 中了
    User user = session.getAttribute().get("user");

        // session 是个会话, 里面记录通信双方的信息 (session 中持有 websocket 的通信连接)
        session.sendMessage(message);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // 这个方法实在 连接出现异常的时候, 被自动调用
        System.out.println("Test 连接异常!");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 这个方法是在连接正常关闭后, 会被自动调用
        System.out.println("Test 连接关闭!");
    }
}

对于注册器, 通过 registerWebSocketHandlers 方法的来注册 Handler (记得给注册器添加注解: @EnableWebSocket)

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Autowired
    private TestWebSocketAPI testWebSocketAPI;

    @Autowired
    private WebSocketAPI webSocketAPI;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 通过本方法, 将创建好的 Handler 类给注册到具体路径上.
        // 此时浏览器可通过 请求路径, 调用到绑定的 Handler 类.
        registry.addHandler(testWebSocketAPI, "/test")
                // 通过注册这个特定的 HttpSession 拦截器, 可以把用户在
                // HttpSession 中添加的 Attribute 键值对
                // 往 WebSocketSession 中添加一份
                .addInterceptors(new HttpSessionHandshakeInterceptor());
    }
}

目录
相关文章
|
1月前
|
设计模式 Java Spring
【Spring源码】WebSocket做推送动作的底层实例是谁
我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁?我们先整体看下整个模块的组织机构。可以看到handleMessage方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并**没有实现**,而是留给了子类去实现。
40 1
【Spring源码】WebSocket做推送动作的底层实例是谁
|
1月前
|
网络协议 C++
websocket数据帧格式
websocket数据帧格式
|
6天前
|
前端开发 JavaScript 安全
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
23 4
|
8天前
|
网络协议 Java 应用服务中间件
|
20天前
WebSocket 协议
【6月更文挑战第6天】
11 1
|
25天前
|
负载均衡 安全 Java
Java一分钟之-WebSocket:实时通信协议
【6月更文挑战第1天】WebSocket是实现客户端与服务器长连接、双向通信的协议,简化实时数据传输。Java中的WebSocket实现基于JSR 356。本文涵盖WebSocket基础(持久连接、双向通信、低延迟)、工作流程、常见问题(安全、连接管理、数据编码)及Java实现示例,强调错误处理、心跳机制和资源管理的最佳实践。
58 6
|
1天前
|
网络协议 Java 应用服务中间件
WebSocket协议
WebSocket协议
4 0
|
28天前
WebSocket 协议
“【5月更文挑战第28天】”
19 1
|
1月前
|
前端开发 网络协议 定位技术
WebSocket协议
【5月更文挑战第3天】WebSocket协议,WebSocket的主要应用场景是什么?
52 10
|
1月前
|
网络协议 前端开发 开发者
WebSocket协议
【5月更文挑战第2天】WebSocket协议
25 4