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());
    }
}
  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值