你知道在springboot中如何使用WebSocket吗_weixin_30273931的博客-程序员秘密

技术标签: 网络  

一、背景

  我们都知道 http 协议只能浏览器单方面向服务器发起请求获得响应,服务器不能主动向浏览器推送消息。想要实现浏览器的主动推送有两种主流实现方式:

  • 轮询:缺点很多,但是实现简单
  • websocket:在浏览器和服务器之间建立 tcp 连接,实现全双工通信

  springboot 使用 websocket 有两种方式,一种是实现简单的 websocket,另外一种是实现STOMP协议。这一篇实现简单的 websocket,STOMP 下一篇在讲。

注意:如下都是针对使用 springboot 内置容器

二、实现

1、依赖引入

  要使用 websocket 关键是@ServerEndpoint这个注解,该注解是 javaee 标准中的注解,tomcat7 及以上已经实现了,如果使用传统方法将 war 包部署到 tomcat 中,只需要引入如下 javaee 标准依赖即可:

<dependency>
  <groupId>javax</groupId>
  <artifactId>javaee-api</artifactId>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>

如使用 springboot 内置容器,无需引入,springboot 已经做了包含。我们只需引入如下依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>1.5.3.RELEASE</version>
    <type>pom</type>
</dependency>

2、注入 Bean

  首先注入一个ServerEndpointExporterBean,该 Bean 会自动注册使用@ServerEndpoint 注解申明的 websocket endpoint。代码如下:

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

3、申明 endpoint

  建立MyWebSocket.java类,在该类中处理 websocket 逻辑

@ServerEndpoint(value = "/websocket") //接受websocket请求路径
@Component  //注册到spring容器中
public class MyWebSocket {


    //保存所有在线socket连接
    private static Map<String,MyWebSocket> webSocketMap = new LinkedHashMap<>();

    //记录当前在线数目
    private static int count=0;

    //当前连接(每个websocket连入都会创建一个MyWebSocket实例
    private Session session;

    private Logger log = LoggerFactory.getLogger(this.getClass());
    //处理连接建立
    @OnOpen
    public void onOpen(Session session){
        this.session=session;
        webSocketMap.put(session.getId(),this);
        addCount();
        log.info("新的连接加入:{}",session.getId());
    }

    //接受消息
    @OnMessage
    public void onMessage(String message,Session session){
        log.info("收到客户端{}消息:{}",session.getId(),message);
        try{
            this.sendMessage("收到消息:"+message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //处理错误
    @OnError
    public void onError(Throwable error,Session session){
        log.info("发生错误{},{}",session.getId(),error.getMessage());
    }

    //处理连接关闭
    @OnClose
    public void onClose(){
        webSocketMap.remove(this.session.getId());
        reduceCount();
        log.info("连接关闭:{}",this.session.getId());
    }

    //群发消息

    //发送消息
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    //广播消息
    public static void broadcast(){
        MyWebSocket.webSocketMap.forEach((k,v)->{
            try{
                v.sendMessage("这是一条测试广播");
            }catch (Exception e){
            }
        });
    }

    //获取在线连接数目
    public static int getCount(){
        return count;
    }

    //操作count,使用synchronized确保线程安全
    public static synchronized void addCount(){
        MyWebSocket.count++;
    }

    public static synchronized void reduceCount(){
        MyWebSocket.count--;
    }
}

4、客户的实现

  客户端使用 h5 原生 websocket,部分浏览器可能不支持。代码如下:

<html>
  <head>
    <title>websocket测试</title>
    <meta charset="utf-8" />
  </head>

  <body>
    <button onclick="sendMessage()">测试</button>
    <script>
      let socket = new WebSocket("ws://localhost:8080/websocket");
      socket.onerror = err => {
        console.log(err);
      };
      socket.onopen = event => {
        console.log(event);
      };
      socket.onmessage = mess => {
        console.log(mess);
      };
      socket.onclose = () => {
        console.log("连接关闭");
      };

      function sendMessage() {
        if (socket.readyState === 1) socket.send("这是一个测试数据");
        else alert("尚未建立websocket连接");
      }
    </script>
  </body>
</html>

三、测试

  建立一个 controller 测试群发,代码如下:

@RestController
public class HomeController {

    @GetMapping("/broadcast")
    public void broadcast(){
        MyWebSocket.broadcast();
    }
}

然后打开上面的 html,可以看到浏览器和服务器都输出连接成功的信息:

浏览器:
Event {isTrusted: true, type: "open", target: WebSocket, currentTarget: WebSocket, eventPhase: 2, …}

服务端:
2018-08-01 14:05:34.727  INFO 12708 --- [nio-8080-exec-1] com.fxb.h5websocket.MyWebSocket          : 新的连接加入:0

点击测试按钮,可在服务端看到如下输出:

2018-08-01 15:00:34.644  INFO 12708 --- [nio-8080-exec-6] com.fxb.h5websocket.MyWebSocket          : 收到客户端2消息:这是一个测试数据

再次打开 html 页面,这样就有两个 websocket 客户端,然后在浏览器访问localhost:8080/broadcast测试群发功能,每个客户端都会输出如下信息:

MessageEvent {isTrusted: true, data: "这是一条测试广播", origin: "ws://localhost:8080", lastEventId: "", source: null, …}

  源码可在 github 下载 上下载,记得点赞,star 哦

本文原创发布于:https://www.tapme.top/blog/detail/2018-08-25-10-38

转载于:https://www.cnblogs.com/wuyoucao/p/10910138.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30273931/article/details/99871215

智能推荐

2020.09.23读MaskedFusion: Mask-based6D object pose estimation_Leo-Ma的博客-程序员秘密

MaskedFusion: Mask-based6D object pose estimation1.背景信息方法网络结构损失函数实验衡量标准结果lineModYCB读后感1.背景信息1.输入数据形式: RGBD图像2.模块化组合框架,每个sub-task都可以用好的模块替换3.个人感觉跟densefusion非常像方法网络结构整个结构分为三个子任务:sub-task1:image segmentation通过语义分割对scene中每个对象检测和分类—&gt;二进制masksub-tas

wangEditor(轻量级web富文本编辑器)学习[全面]_Yvette_QIU的博客-程序员秘密

了解:我为什么要做富文本编辑器【wangEditor5个月总结】轻量级富文本编辑器wangEditor源码结构介绍wangEditor——介绍、使用手册学习:wangEditor包的下载 —— 轻量级web富文本框【试用:http://wangEditor.github.io/】wangEditor上传本地视频参考:wangEditor的使用【主要讲脚本代码】...

7-8 点赞狂魔 (25分)_lc_wei的博客-程序员秘密

**7-8 点赞狂魔 (25分)**微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明显的特性。本题就要求你写个程序,通过统计每个人点赞的不同标签的数量,找出前3名点赞狂魔。输入格式:输入在第一行给出一个正整数N(≤100),是待统计的用户数。随后N行,每行列出一位用户的点赞标签。格式

微信小程序创建自定义带图标的底部弹框_也还不是会员的博客-程序员秘密

正常情况下,小程序中要从页面底部弹出选择框的话,首要选择是wx.showActionSheet({itemList: ['A', 'B', 'C'], success: function(res) { }})该方法运行后样式在iOS机子上显示为如下图,在android机子上显示的是没有“取消”按钮的,并且弹出框并不在页面底部,而是在页面中间。现在我想要的效果是...

Mac下Nginx 本地 HTTPS 配置(转载)_暴走的奶糖的博客-程序员秘密

1、首先确保机器上安装了openssl和openssl-develnpm install opensslnpm install openssl-devel (安装报错 导致我没安装成功,但是也还是配置成功了 我感觉这个没啥用)2、然后自己给自己颁发证书cd /usr/local/etc/nginx/ (这个是证书的安装目录,建议放置在 nginx 根目录 )openssl genrsa...

随便推点

句柄与指针的区别(一)_go 语言指针和句柄的区别_yuandm819的博客-程序员秘密

句柄其实就是指针,但是他和指针最大的不同是:给你一个指针,你可以通过这个指针做任何事情,也许是好事,也许是通过这个指针破坏内存,干一些捣乱的事情。这个我想大家都会碰到过,因为乱用指针导致程序崩溃句柄就没有这个缺点,通过句柄,你只能干一些windows让你干的事情(调用一些api函数等等),没有了指针的坏处。    句柄是一些表的索引也就是指向指针的指针,句柄和指针都是地址,句柄是Win

MindSpore21天实战营(1):基于MindSpore Lite开发目标检测的安卓APP实战_基于华为mindspore框架的目标物体的检测_昇思MindSpore的博客-程序员秘密

转载地址:MindSpore21天实战营(1):基于MindSpore Lite开发目标检测的安卓APP实战_MindSpore_昇腾论坛_华为云论坛作者:胡琦基于MindSpore Lite开发目标检测的安卓APP实战“零”基础的Copy攻城狮居然敢实战MindSpore Lite开发目标检测的安卓APP,谁给的勇气?是梁静茹吗?前言大家好,我是Copy攻城狮胡琦,有幸参与华为业界首个全场景AI实战营,今天是第1天学习,也是从小白到大牛迈出的第一步,俗话说“万事难开头,只怕有心人”,但

ArrayList ListItr_csdn产品小助手的博客-程序员秘密

为什么80%的码农都做不了架构师?&gt;&gt;&gt; ...

C语言该学的地方(项目经验)【转载】_c语言项目经验怎么写_yangss2006的博客-程序员秘密

一,基础部分:这里分两大块,一块就是数据结构,另一块是算法:1.数据结构要看的是:基本的数据类型,int ,float,double, char ,指针;1)扩展数据类型struct,枚举,联合;用户数据定义数据结构:链表,数组.这里要掌握几种基本数据类型的用法!2)指针,及用指针来处理字符串,因为C里面没有字符串这种类型的变量,其实在其它语言里学习过字符串可能会有先入为主的观点,认为字符串也是一

Swift - 纯代码实现页面segue跳转,以及参数传递_全沾边工程师的博客-程序员秘密

转自:http://www.hangge.com/blog/cache/detail_720.html下面通过一个例子说明如何在代码中进行segue页面的切换,以及参数的传递。样例功能如下: 1,主界面中是一个列表(这个列表是在代码中实现) 2,点击列表项时,界面会切换到详情页面,同时传递改列表项的值到详细页面。效果图如下: 原文:Swift - 纯代码实现页面segue跳转,以及参数传递 原

ios上传base64图片的问题_writefile:fail base64 encode error ios_二十五岁的我的博客-程序员秘密

function image($image){$imageName = “20188_” . date(“His”, time()) . “_” . rand(1111, 9999) . ‘.png’;$image = str_replace(" ","+",$image);if (strstr($image, ",")) { $image = explode(',', $ima...

推荐文章

热门文章

相关标签