WebSocket笔记

发布于:2025-02-10 ⋅ 阅读:(30) ⋅ 点赞:(0)

此笔记来自于黑马的WebSocket教程

1. WebSocket

1.1 WebSocket介绍

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求
做出应答处理。

这种通信模型有一个弊端:HTTP协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步
AJAX 请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者HTTP连接始终打开)。

http协议:

image-20250106174132202

WebSocket客户端

image-20250106174232009

1.2 websocket协议

本协议有两部分:握手和数据传输。

握手是基于http协议的。

来自客户端的握手看起来像如下形式:

GET ws://localhost/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-key: dGhlIHNHbXBsZSBub25jZQ==
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Version: 13

来自服务器的握手看起来像如下形式:

HTTP/1.1 101 Switching Protocols
Upgrade:websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhzRbK+xOo=
Sec-websocket-Extensions: permessage-deflate

字段说明:

头名称
Connection: Upgrade 标识该HTTP请求是一个协议升级请求
Upgrade: WebSocket 协议升级为WebSocket协议
Sec-WebSocket-Version: 13 客户端支持WebSocket的版本
Sec-WebSocket-Key: 客户端采用base64编码的24位随机字符序列,服务器接收客户端HTTP协议升级的证明。要求服务器响应一个对应加密的Sec-WebSocket-Accept头信息作为应答
Sec-WebSocket-Extensions 协议扩展类型

1.3 客户端(浏览器实现)

1.3.1 websocket对象

实现 WebSockets 的 Web 浏览器将通过 WebSocket 对象公开所有必需的客户端功能(主要指支持Htm15的浏览器)
以下 API 用于创建 WebSocket对象:

var ws = new websocket(url);

参数 url 格式说明:ws://ip地址:端口号/资源名称

1.3.2 websocket事件

WebSocket 对象的相关事件

事件 事件处理程序 描述
open websocket对象.onopen 连接建立时触发
message websocket对象.onmessage 客户端接收服务端数据时触发
error websocket对象.onerror 通信发生错误时触发
close websocket对象.onclose 连接关闭时触发

1.3.3 WebSocket方法

WebSocket 对象的相关方法

方法 描述
send() 使用连接发送数据

1.4 服务端实现

Tomcat 的 7.0.5 版本开始支持 WebSocket, 并且实现了 Java WebSocket规范(JSR356)。

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

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

  • 第一种是编程式,即继承类 javax.websocket.Endpoint 并实现其方法。

  • 第二种是注解式,即定义一个 POJO,并添加 @ServerEndpoint相关注解。

Endpoint 实例在 webSocket 握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在 Endpoint 接口中明确
定义了与其生命周期相关的方法,!规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:

方法 含义描述 注解
onClose 当会话关闭时调用 @OnClose
onOpen 当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法 @OnOpen
onError 当连接过程中异常时调用 @OnError

服务端如何接受客户端发送的数据呢?

通过为 Session 添加 M ssageHandler 消息处理器来接收消息,当采用注解方式定义 Endpoint 时,我们还可以通过@OnMessage注解指定接收消息的方法。

服务端如何推送数据给客户端呢?

发送消息则由 RemoteEndpoint 完成,其实例由 Session 维护,根据使用情况,我们可以通过
Session.getBasicRemote 获取同步消息发送的实例,然后调用其 sendXxx()方法就可以发送消息,,可以通过 Session.getAsyncRemote 获取异步消息发送实例。

服务端代码:

@ServerEndopoint("/robin")
public class ChatEndPoint() {
    private static Set<Chat> webSocketSet = new HashSet<>();
    
    private session session;
    
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println("接收的消息是: " + message);
        System.out.println(session);
        
        // 将消息发送给其他的用户
        for (Chat chat : webSocketSet) {
            if (chat != this) {
                chat.session.getBasicRemote().sendText(message);
            }
        }
    }
    
    @OnOpen
    public void onOpen(Session session) {
		this.session = session;
        webSocketSet.add(this);
    }
    
    @OnClose
    public void onClose(Session seesion) {
        System.out.println("连接关闭了。。。。。");
    }
    
    @OnEoor
    public void onError(Session session, Throwable error) {
		System.out.println("出错了。。。。。" + error.getMessage());
    }
} 

2 基于 WebSocket的网页聊天室

2.1 需求

通过 websocket 实现一个简易的聊天室功能

  1. 登录聊天室

image-20250106192359099

  1. 登录之后,进入聊天页面进行聊天

登陆成功后,呈现出以后的效果:

image-20250106192457592

当我们想和李四聊天时就可以点击 好友列表 中的 李四,效果如下:

image-20250106192550091

接下来就可以进行聊天了,张三的界面如下:

image-20250106192639749

李四的界面如下:

image-20250106192658595

2.2 实现流程

image-20250106193023498

2.3 消息格式

  • 客户端 --> 服务端

{“toName”:“张三”,“message”:“你好”}

  • 服务端 --> 客户端
    • 系统消息格式:{“isSystem”:true, “fromName”:null, “message”:[“李四”,“王五”]}
    • 推送给某一个的消息格式:{“isSystem”:true,“fromName”:“张三”,“message”:“你好”}2

“toName”:“张三”,“message”:“你好”}

  • 服务端 --> 客户端
    • 系统消息格式:{“isSystem”:true, “fromName”:null, “message”:[“李四”,“王五”]}
    • 推送给某一个的消息格式:{“isSystem”:true,“fromName”:“张三”,“message”:“你好”}2