SpringBoot之集成WebSocket

websocket是什么不做介绍。开发环境:jdk1.8,win7_64旗舰版,idea

1、初始化一个springboot项目

2、加入websocket依赖

<!-- springboot的websocket依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

pom.xml如下:

<dependencies>
        <!-- 模板引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!---->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>

        <!-- lombok工具 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 内置tomcat -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

3、编写websocket的服务端

3.1、WebSocketEndPoint是websocket服务端的核心

@PathParam是javax.websocket.server下的注解,是将路径中绑定的占位符的值取出来

  在url中使用key和name,是想通过key和name对websocket的连接进行访问控制,这个key可以是用户登录后服务器给用户的令牌,通过令牌和和name进行权限验证(自己写拦截器或者继承权限框架实现),还可以通过key和name生成唯一值来进行在线websocket

连接的维护<(key+name), websocketSession>, 当然,我在这里没有这样做。

package com.geniuses.sewage_zero_straight.net.websocket;

import com.geniuses.sewage_zero_straight.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;

import static com.geniuses.sewage_zero_straight.net.websocket.WebSocketPool.*;
import static com.geniuses.sewage_zero_straight.net.websocket.WebSocketHandler.createKey;

@Slf4j
@Component
@ServerEndpoint("/net/websocket/{key}/{name}")//表明这是一个websocket服务的端点
public class WebSocketEndPoint {

    private static UserService userService;

    @Autowired
    public void setUserService(UserService userService){
        WebSocketEndPoint.userService = userService;
    }

    @OnOpen
    public void onOpen(@PathParam("key") String key, @PathParam("name") String name,  Session session){
        log.info("有新的连接:{}", session);
        add(createKey(key, name), session);
        WebSocketHandler.sendMessage(session, key + name);
        log.info("在线人数:{}",count());
        sessionMap().keySet().forEach(item -> log.info("在线用户:", item));
        for (Map.Entry<String, Session> item : sessionMap().entrySet()){
            log.info("12: {}", item.getKey());
        }
    }

    @OnMessage
    public void onMessage(String message){
        log.info("有新消息: {}", message);
    }

    @OnClose
    public void onClose(@PathParam("key") String key, @PathParam("name") String name,Session session){
        log.info("连接关闭: {}", session);
        remove(createKey(key, name));
        log.info("在线人数:{}",count());
        sessionMap().keySet().forEach(item -> log.info("在线用户:", (item.split("@"))[1]));
        for (Map.Entry<String, Session> item : sessionMap().entrySet()){
            log.info("12: {}", item.getKey());
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable){
        try {
            session.close();
        } catch (IOException e) {
            log.error("onError Exception: {}", e);
        }
        log.info("连接出现异常: {}", throwable);
    }

}

  3.2、WebSocketPool是websocket的在线连接池

package com.geniuses.sewage_zero_straight.net.websocket;

import lombok.extern.slf4j.Slf4j;

import javax.websocket.Session;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class WebSocketPool {

    //在线用户websocket连接池
    private static final Map<String, Session> ONLINE_USER_SESSIONS = new ConcurrentHashMap<>();

    /**
     * 新增一则连接
     * @param key
     * @param session
     */
    public static void add(String key, Session session){
        if (!key.isEmpty() && session != null){
            ONLINE_USER_SESSIONS.put(key, session);
        }
    }

    /**
     * 根据Key删除连接
     * @param key
     */
    public static void remove(String key){
        if (!key.isEmpty()){
            ONLINE_USER_SESSIONS.remove(key);
        }
    }

    /**
     * 获取在线人数
     * @return
     */
    public static int count(){
        return ONLINE_USER_SESSIONS.size();
    }

    /**
     * 获取在线session池
     * @return
     */
    public static Map<String, Session> sessionMap(){
        return ONLINE_USER_SESSIONS;
    }
}

3.3、WebSocketHandler是websocket的动作处理工具

package com.geniuses.sewage_zero_straight.net.websocket;

import lombok.extern.slf4j.Slf4j;

import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import java.io.IOException;
import static com.geniuses.sewage_zero_straight.net.websocket.WebSocketPool.sessionMap;

@Slf4j
public class WebSocketHandler {

    /**
     * 根据key和用户名生成一个key值,简单实现下
     * @param key
     * @param name
     * @return
     */
    public static String createKey(String key, String name){
        return key + "@" + name;
    }

    /**
     * 给指定用户发送信息
     * @param session
     * @param msg
     */
    public static void sendMessage(Session session, String msg){
        if (session == null)
            return;
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null)
            return;
        try {
            basic.sendText(msg);
        } catch (IOException e) {
            log.error("sendText Exception: {}", e);
        }
    }

    /**
     * 给所有的在线用户发送消息
     * @param message
     */
    public static void sendMessageAll(String message){
        log.info("广播:群发消息");
        sessionMap().forEach((key, session) -> sendMessage(session, message));
    }
}

4、前端访问实现

4.1、index.html,页面引用了jquery和bootstrap样式,请自行应用

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.springframework.org/schema/mvc">
<head>
    <meta charset="UTF-8">
    <title>chat room websocket</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    <script th:src="@{/js/jquery-3.3.1.min.js}" ></script>
</head>
    <body class="container" style="width: 60%">
        <div class="form-group" ></br>
            <h5>聊天室</h5>
            <textarea id="message_content"  class="form-control"  readonly="readonly" cols="50" rows="10"></textarea>
        </div>
        <div class="form-group" >
            <label for="in_user_name">用户姓名 &nbsp;</label>
            <input id="in_user_name" value="" class="form-control" /></br>
            <button id="user_join" class="btn btn-success" >加入聊天室</button>
            <button id="user_exit" class="btn btn-warning" >离开聊天室</button>
        </div>
        <div class="form-group" >
            <label for="in_room_msg" >群发消息 &nbsp;</label>
            <input id="in_room_msg" value="" class="form-control" /></br>
            <button id="user_send_all" class="btn btn-info" >发送消息</button>
        </div>
    </body>
    <<script type="text/javascript">
        $(document).ready(function(){
            var urlPrefix =‘ws://192.168.2.156:8080/net/websocket/12/‘;
            var ws = null;
            $(‘#user_join‘).click(function(){
                var username = $(‘#in_user_name‘).val();
                var url = urlPrefix + username;
                ws = new WebSocket(url);
                ws.onopen = function () {
                    console.log("建立 websocket 连接...");
                };
                ws.onmessage = function(event){
                    //服务端发送的消息
                    $(‘#message_content‘).append(event.data+‘\n‘);
                };
                ws.onclose = function(){
                    $(‘#message_content‘).append(‘用户[‘+username+‘] 已经离开聊天室!‘ + ‘\n‘);
                    console.log("关闭 websocket 连接...");
                }
            });
            //客户端发送消息到服务器
            $(‘#user_send_all‘).click(function(){
                var msg = $(‘#in_room_msg‘).val();
                if(ws){
                    ws.send(msg);
                }
            });
            // 退出聊天室
            $(‘#user_exit‘).click(function(){
                if(ws){
                    ws.close();
                }
            });
        })
    </script>
</html>

  4.2、页面访问控制器,由此来访问index.html

package com.geniuses.sewage_zero_straight.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("/view")
@Controller
public class ViewController {

    /**
     * 返回首页
     * @return
     */
    @GetMapping("/index")
    public String index(){
        return "index";
    }
}

5、websocket配置

package com.geniuses.sewage_zero_straight.config;

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

@Configuration
@EnableWebSocket
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

6、注意:

在使用了@ServerEndpoint注解的类是无法直接使用@Autowired的,因为@ServerEndpoint表明当前类是websocket的服务端点,在spring容器启动时会初始化一次该类,当有新的websocket连接的时候,也会进行该类实例的创建(每一次连接时都会创建一个实例),所以在第二次往后创建该类实例的时候,就无法进行有效的@Autowired了,此时发现,即便第一次注入是有效的,但是也没有什么用。这个时候,将需要注入的变量置为类的变量,提供一个set方法(该方法为实例方法),在set方法上面进行依赖注入,这样就可以进行有效的注入了。

7、这里只是websocket的简单实现,更多情况...

原文地址:https://www.cnblogs.com/threadj/p/10552904.html

时间: 2024-11-10 22:53:04

SpringBoot之集成WebSocket的相关文章

springboot集成websocket的两种实现方式

WebSocket跟常规的http协议的区别和优缺点这里大概描述一下 一.websocket与http http协议是用在应用层的协议,他是基于tcp协议的,http协议建立链接也必须要有三次握手才能发送信息.http链接分为短链接,长链接,短链接是每次请求都要三次握手才能发送自己的信息.即每一个request对应一个response.长链接是在一定的期限内保持链接.保持TCP连接不断开.客户端与服务器通信,必须要有客户端发起然后服务器返回结果.客户端是主动的,服务器是被动的. WebSocke

springboot elasticsearch 集成注意事项

文章来源: http://www.cnblogs.com/guozp/p/8686904.html 一 elasticsearch基础 这里假设各位已经简单了解过elasticsearch,并不对es进入更多的,更深层次的解释,如有必要,会在写文章专门进行es讲解. Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎.无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进.性能最好的.功能最全的搜索引擎库. 但是,Lucene只是一个库.想要使用它,你必

Springboot Application 集成 OSGI 框架开发

内容来源:https://www.ibm.com/developerworks/cn/java/j-springboot-application-integrated-osgi-framework-development/index.html Springboot Application 集成 OSGI 框架开发 张 莹莹2018 年 4 月 02 日发布 WeiboGoogle+用电子邮件发送本页面 0 Java 类加载器 启动类加载器 (Bootstrap ClassLoader) 是 Ja

springboot集成websocket

websocket是全双工通信协议,目前html5支持,如果是app端的话可能不支持,建议app端实现通过tcp握手长连接实现通信,这里暂不研究. 首先websocket是一个协议,需要了解一下 第一步先引入starter 1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-websocket</artifactId

springboot 集成 websocket

1.介绍 WebSocket是HTML5新增的协议,它的目的是在浏览器和服务器之间建立一个不受限的双向通信的通道.实时推送数据/通知到浏览器 方法一 引入WebSocket依赖包 ````xml org.springframework.boot spring-boot-starter-websocket org.springframework.boot spring-boot-starter-webflux 编写 WebSocket 实现方法 @ServerEndpoint(value = "/

springBoot框架shedlock+websocket功能冲突的解决方案

背景描述: 项目开发中首先遇到在集群模式部署服务端时,多个节点的定时任务在各自的节点处均会执行一遍的问题,针对这个单一问题,上一篇文章https://www.cnblogs.com/Iris1998/p/11413099.html给出了springboot集成shedlock的解决方案,但一段时间后项目中有了新的需求,想要实现消息主动推到客户端的功能,单从功能上来看可以采用springboot自带的websocket实现,但开发过程遇到很奇怪的报错,报错信息如下 {"time":&qu

SpringBoot 04_集成SpringDataJpa

你还在为项目的集成头疼吗?你还在为管理大量的配置文件烦恼吗?如果是,用SpringBoot吧!今天主要介绍如果在SpringBoot的基础上创建一个集成了SpringDataJpa的项目,至于SpringDataJpa是什么,如果用过Hibernate的同学想必都在为不用写sql在庆幸吧,想要了解SpringDataJpa的话,我以后会写相关的博客分享给大家. 下面就带大家来创建一个SpringBoot + SpringDataJpa的项目,创建的环境: 开发工具 :IDEA SpringBoo

springboot(二)集成mybatis

全部内容:1,集成mybatis 2,分页查询使用分页插件pagehelper工程代码:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo 1.添加集成mybatis的依赖 <!--最新版本,匹配spring Boot1.5及以上的版本--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId

集成websocket即时通讯 java聊天源码

获取[下载地址]     [免费支持更新]三大数据库 mysql  oracle  sqlsever   更专业.更强悍.适合不同用户群体[新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统] A 集成代码生成器 [正反双向(单表.主表.明细表.树形表,开发利器)+快速构建表单; freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块B 集成阿里巴巴数据库连接池druid;  数据库连接池  阿里巴巴的 druid.