WebSocket的故事(六)—— Springboot中,实现更灵活的WebSocket

栏目: Java · 发布时间: 8年前

内容简介:WebSocket的故事系列计划分五大篇六章,旨在由浅入深的介绍WebSocket以及在Springboot中如何快速构建和使用WebSocket提供的能力。本系列计划包含如下几篇文章:

WebSocket的故事系列计划分五大篇六章,旨在由浅入深的介绍WebSocket以及在Springboot中如何快速构建和使用WebSocket提供的能力。本系列计划包含如下几篇文章:

第一篇,什么是WebSocket以及它的用途

第二篇,Spring中如何利用STOMP快速构建WebSocket广播式消息模式

第三篇,Springboot中,如何利用WebSocket和STOMP快速构建点对点的消息模式(1)

第四篇,Springboot中,如何利用WebSocket和STOMP快速构建点对点的消息模式(2)

第五篇,Springboot中,实现网页聊天室之自定义WebSocket消息代理

第六篇,Springboot中,实现更灵活的WebSocket

本篇的主线

本篇是这个系列的最后一篇,将介绍另一种实现WebSocket的方式。仍然会以一个简单聊天室为例子进行讲述。至此我们也可以根据具体情况,选择不同的实现方式。

本篇适合的读者

想了解如何在Springboot上自定义实现更为复杂的WebSocket产品逻辑的各路有志青年。

使用Tomcat提供的WebSocket支持

早在 Java EE 7 时,就发布了 JSR356 规范。Tomcat7.0.47开始,也支持了统一的 WebSocket 接口。在使用 Springboot 时,也可以轻松的使用 Tomcat 提供的这些API。今天我们就来体验一把 Tomcat 实现的 WebSocket 。

1. 引入依赖

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
复制代码

Springboot内置了tomcat,我们直接引入spring的这个高级组件即可。顺便多说一句,Springboot的高级组件会自动引用基础的组件,像 spring-boot-starter-websocket 就引入了 spring-boot-starter-web 和 spring-boot-starter ,所以不要重复引入。

2. 使用@ServerEndpoint创建WebSocket Endpoint

首先要注入 ServerEndpointExporter ,这个Bean会自动注册使用了 @ServerEndpoint 注解声明的WebSocket Endpoint。

package com.draw.wsdraw.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
复制代码

然后,我们动手实现WebSocket服务的实现类,这里是 WebSocketServer ,注意别忘了用 @ServerEndpint 和 @Component 声明下。虽然 @Component 默认是单例模式的,但Springboot还是会为每个WebSocket连接初始化一个 Bean ,所以可以用一个静态Map保存起来。换句话说,每当有一个用户向服务器发起连接时,都会创建一个 WebSocketServer 对象,将此对象按 roomId 保存在 HashMap 中,方便后续使用。

创建ServerEndpoint时,需要对应实现其所需的几个功能性方法: OnOpen、OnMessage、OnClose、OnError 。

@OnOpen
@OnMessage
@OnClose
@OnError

重写上述方法,即可实现WebSocket的服务端业务逻辑。

@ServerEndpoint("/webSocket/{roomId}")
@Component
public class WebSocketServer {
    private static ConcurrentHashMap<String, List<WebSocketServer>> webSocketMap =
            new ConcurrentHashMap<>(3);

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    //接收roomId
    private String roomId = "";

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config, @PathParam("roomId") String roomId) {
        if (roomId == null || roomId.isEmpty()) return;
        this.session = session;
        this.roomId = roomId;
        addSocketServer2Map(this);
        try {
            sendMessage("连接成功", true);
        } catch (IOException e) {
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        List<WebSocketServer> wssList = webSocketMap.get(roomId);
        if (wssList != null) {
            for (WebSocketServer item : wssList) {
                if (item.session.getId().equals(session.getId())) {
                    wssList.remove(item);
                    if (wssList.isEmpty()) {
                        webSocketMap.remove(roomId);
                    }
                    break;
                }
            }
        }
    }
    
    /**
     * 收到客户端消息后调用的方法
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //群发消息
        String msg = filterMessage(message);
        if (msg != null) {
            sendInfo(msg, roomId, session);
        }
    }

    /**
     * 发生错误时,调用的方法
     */
    @OnError
    public void onError(Session session, Throwable error) {
    }

复制代码

这样,服务端的代码就实现完了,这里仅贴出来部分源码,文后会给出项目源码地址。

3. 实现客户端页面

<script type="text/javascript">
    var ws;
    
    function setConnected(connected){
        document.getElementById('connect').disabled = connected;
        document.getElementById('disconnect').disabled = !connected;
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
        $("#response").html();
    }

    function connect(){
        var roomId = $('#roomId').val();
        ws = new WebSocket('ws://localhost:8080/webSocket/' + roomId);
        ws.onopen = WSonOpen;
        ws.onmessage = WSonMessage;
        ws.onclose = WSonClose;
        ws.onerror = WSonError;
    }

    function WSonOpen() {
        var message = {
            name:'Server',
            chatContent:'成功连接'
        }
        setConnected(true);
        showResponse(message)
    };

    function WSonMessage(event) {
        var message = {
            name:'Server',
            chatContent:event.data
        }
        showResponse(message)
    };

    function WSonClose() {
        var message = {
            name:'Server',
            chatContent:'已断开'
        }
        showResponse(message)
    };

    function WSonError() {
        var message = {
            name:'Server',
            chatContent:'连接错误!'
        }
        showResponse(message)
    };

    function disconnect(){
        ws.close()
        setConnected(false);
        console.log("Disconnected");
    }

    function sendMessage(){
        var chatContent = $("#chatContent").val();
        var roomId = $('#roomId').val();
        ws.send(JSON.stringify({'roomId':roomId,'chatContent':chatContent}))
    }

    function showResponse(message){
         var response = $("#response").val();
         $("#response").val(response+message.name+': '+message.chatContent+'\n');
    }
</script>
复制代码

客户端页面实现了简单的连接、断开和消息发送功能。这部分就不详细介绍了。

4. 演示截图

WebSocket的故事(六)—— Springboot中,实现更灵活的WebSocket

源码地址

本篇的源码地址:

另一种WebSocket的实现方式

总结

本篇直接使用了Tomcat提供的WebSocket,也是一种相对灵活的实现方式,只需要按照上述步骤来实现即可。集中精力编写业务逻辑代码。

整个一个系列下来,我们介绍了几种实现WebSocket的方式,有的集成度高,有些相对灵活。大家可以按实际业务需求来选取合适的方式。至此,这个系列就结束了。感谢大家阅读。

小铭出品,必属精品

欢迎关注xNPE技术论坛,更多原创干货每日推送。

WebSocket的故事(六)—— Springboot中,实现更灵活的WebSocket

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Clean Architecture

Clean Architecture

Robert C. Martin / Prentice Hall / 2017-9-20 / USD 34.99

Practical Software Architecture Solutions from the Legendary Robert C. Martin (“Uncle Bob”) By applying universal rules of software architecture, you can dramatically improve developer producti......一起来看看 《Clean Architecture》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具