WebSocket实战:打造实时在线聊天室

发布于:2025-07-11 ⋅ 阅读:(26) ⋅ 点赞:(0)

一、websocket的概念

我们应该了解的是websocket是ISO参考模型的应用层的协议之一

它是一种在TCP连接上进行全双工也就是双向通信的协议。有了这个协议,客户端和服务器之间就可以随时互相发送数据,不再像传统HTTP那样一问一答了也就是请求响应。当客户端和服务器的连接一旦建立,不手动关闭或者设置自动关闭就会一直保持。

  • 全双工:允许数据在两个方向同时进行传输
  • 半双工:允许数据在两个方向上传输,但是某个时间段内只允许一个方向的传输

二、为什么我们需要WebSocket?

比如我们想要实现网上聊天室或者网上棋类在线对战游戏

HTTP 是“请求-响应”模式: 客户端发起请求,服务器被动响应。

但这种模式有几个问题:

问题 举例 结果
无法实时 比如股票价格变化,用户得不断刷新页面才看到新数据 用户体验差
请求多、浪费资源 比如每隔500ms轮询一次服务器 增加带宽、服务器压力
响应延迟 服务端不能主动通知客户端 数据总是滞后

轮询

如果我们使用websocket

连接过程如下

同理客户端2的websocket的建立流程也如上图,握手后就“切换”成 WebSocket 协议,不再是 HTTP 请求

基于websocket的网上五子棋对战的大致流程

WebsocketAPI

客户端浏览器API(了解即可)

1.websocket对象创建(javascript)

let was = new WebSocket(URL)

// URL 说明

// 格式 : 协议://ip地址/访问路径

// 协议 : 协议名称为ws 也就是websocket

2.websocket对象相关事件
事件 事件处理程序 描述
open ws.onopen 连接建立时触发
message ws.onmessage 客户端接收到服务器发送的数据时触发
close ws.onclose 连接关闭时触发
error ws.onerror 连接异常时触发
3.websocket对象提供的方法

ws.send() 通过websocket对象调用该方法发送数据给服务端(前后端连接使用)

<script>
    let ws = new WebSocket("ws://localhost/chat");
	ws.onopen = function(){
        console.log("connect successfully");
    }
	ws.onmessage() = function(event){
        //通过event.data可以获取到服务器发送给客户端的数据
        console.log("event data:" + event.data);
    }
	ws.onerror() = function(){
        console.log("error connection");
    }
	ws.onclose() = function(){
        console.log("close connection: ");
    }
    
</script>    

服务端后端API

Java WebSocket 应用程序由一系列的Endpoint组成。Endpoint是一个Java类,代表WebSocket连接的一段,对于服务端,我们可以认为是处理具体WebSocket消息的接口。

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

  • 编程式:继承类javax.websocket.Endpoint并实现其方法        
  • 注解式:即定义一个POJO,并添加@ServerEndpoint相关注解

Endpoint具有一定的生命周期我们并不深入研究

在其声明周期中有以下方法可使用:

方法 描述 注解
onOpen() 当开启一个新的会话时调用,该方法是客户端与服务端握手成功后调用的方法 @OnOpen
onClose() 当会话关闭时调用 @OnClose
onError() 当连接过程异常时调用 @OnError
onMessage()                当会话建立某一方或者双发发送消息时调用的方法 @OnMessage
@ServerEndpoint("/chat")
//在@ServerEndpoint注解中加入路径
@Component
public class ChatEndpoint {
    @OnOpen
    //连接建立时被调用
    //这里的Session是WebSocket的Session而非Http的HttpSession
    public void onOpen(Session session, EndpointConfig config){
    }
    @OnMessage
    //message接收到客户端发来的信息
    public void onMessage(String message){
    }
    @OnClose
    public void onClose(Session session){
    }
}

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

  • 编程式:通过添加MessageHandler消息处理器来接受消息 (了解即可)      
  • 注解式:在定义的Endpoint时,通过@OnMessage注解指定接收消息的方法

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

发送消息则由RemoteEndpoint完成,其实例由Session维护

  • 通过session.getBasicRemote获取同步消息发送的实例,然后调用sendXxx()方法发送消息
// 发送文本消息(同步)
session.getBasicRemote().sendText("Hello, synchronous message!");

// 发送二进制消息(同步)
ByteBuffer buffer = ByteBuffer.wrap(new byte[]{1, 2, 3});
session.getBasicRemote().sendBinary(buffer);

// 发送Pong消息(同步)
session.getBasicRemote().sendPong(buffer);

通过session.getAsyncRemote获取异步消息发送实例,然后调用sendXxx()方法发送消息

// 发送文本消息(异步)
session.getAsyncRemote().sendText("Hello, asynchronous message!");

// 发送二进制消息(异步)
ByteBuffer buffer = ByteBuffer.wrap(new byte[]{4, 5, 6});
session.getAsyncRemote().sendBinary(buffer);

// 发送Pong消息(异步)
session.getAsyncRemote().sendPong(buffer);

 

在线聊天室实现

具体流程分析

消息格式

客户端发给服务端

Json格式

{"toName":"zjk" ,  "message" : "你好"}

服务端发给客户端

  • 系统消息格式:

{"system":true , "fromName": null , "message":["张三","王五"]} 

  • 推送给某一个用户的消息格式:

{"system":false, "fromName": "张三", "message":"你好"}

代码实现

引入必要的依赖坐标

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

编写配置类

在 Spring 框架中,@Bean 是一个用于告诉 Spring 容器 我要自己手动创建一个对象并交给你管理的注解。

它常用于配合 @Configuration 注解的类中,用来显式声明一个 Spring 容器中的 Bean,用于方法上,方法返回值被注册为 Bean。

@Component注解是一个用于告诉Spring容器 你帮我来创建一个对象并管理,Spring 自动扫描并创建,类被扫描注册为 Bean。

/**
 * WebSocket配置类
 * @author 赵家康
 * @date 2025/7/8
 */
@Configuration
public class WebSocketConfig {

    @Bean
    //注入ServerEndpointExporter,自动注册使用@ServerEndpoint注解
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

编写配置类,用于获取Httpsession对象

/**
 * 配置类
 * 用于从 ServerEndpointConfig 中 获取 httpSession 对象
 */
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request,
        HandshakeResponse response) {

        HttpSession httpSession = (HttpSession)request.getHttpSession();
        //将 httpsession 存入 ServerEndpointConfig对象,方便于websocket在OnOpen()建立链接后获取
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

在@ServerEndpoint注解中引入配置器

@ServerEndpoint(value = "/caht" , configurator = GetHttpSessionConfigurator.class)

什么是 ServerEndpointExporter

它是 Spring Boot 内嵌容器 + WebSocket 结合使用时必须的一个类

  • 它会在应用启动时扫描所有使用了 @ServerEndpoint("/xxx") 注解的类;

  • 并把这些类注册成真正的 WebSocket 端点(endpoint);

  • 也就是说,没有它,Spring Boot 不知道你的 @ServerEndpoint 类是干啥的,WebSocket 就无法正常工作。

项目效果

项目gitee地址:赵家康/基于WebSocket的网络聊天室

已实现同一个用户不可在多端同时登录


网站公告

今日签到

点亮在社区的每一天
去签到