websocket是客户端和服务端的通信,所以它肯定有客户端和服务端

这里客户端直接用原生的html5来编写

首先当然是先引入对应的依赖

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

然后注入相应的bean

package com.chan.wechatshop.config;

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

@Component
public class WebSocketConfig {

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

}

js中websocket建立连接后websocket对象的几种状态

可以用websocket.readyState查看

0 :对应常量CONNECTING (numeric value 0),
正在建立连接连接,还没有完成。The connection has not yet been established.

1 :对应常量OPEN (numeric value 1),
连接成功建立,可以进行通信。The WebSocket connection is established and communication is
possible.

2 :对应常量CLOSING (numeric value 2)
连接正在进行关闭握手,即将关闭。The connection is going through the closing handshake.

3 : 对应常量CLOSED (numeric value 3)
连接已经关闭或者根本没有建立。The connection has been closed or could not be opened.

weosocket接收客户端消息,发送消息给客户端实现类

这里要注意,每次建立websocket连接的时候都会新建一个下面路由中的WebSocket class对象,所以在里面注入springbean的时候是没有经过spring的依赖注入也就没有bean实例的,所以需要调用springbean的话需要将属性声明成static然后再注入,或者新增一个工具类,用static注入.

可以注意到下面demo中的webSockets属性就是static的,就是这个原因

package com.chan.wechatshop.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint("/websocket")
@Slf4j
public class WebSocket {

    private Session session;

    //普通的Set存不进
    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();

    /**
     * 对应前端的相应事件
     * @param session
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSockets.add(this);
        log.info("[websocket消息] 有新的连接,总数:{}",webSockets.size());
    }

    /**
     * 关闭的时候移除
     */
    @OnClose
    public void onClose(){
        webSockets.remove(this);
        log.info("[websocket消息] 连接断开,总数:{}",webSockets.size());
    }

    @OnMessage
    public void onMessage(String message){
        log.info("[websocket消息] 收到客户端发来的消息:{}",message);

    }

    public void sendMessage(String message){
        for(WebSocket webSocket :webSockets){
            log.info("[websocket消息] 广播消息,message={}",message);
            try {
                            webSocket.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
                log.error("[websocket消息] 广播消息,出现异常,{}",e.getMessage());
            }

        }
    }


}

要使用的地方注入进去就可以了,然后调用sendMessage广播消息

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_spring

前端也就是客户端

js中websocket的4种状态

websocket.readyState

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_spring_02

连接服务端地址

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_websocket_03

前端页面打开的时候自动执行这一段js代码,这时可以在服务端看到已经连上了websocket

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_spring boot_04

客户端也打印输出了一下控制台,也可以看到websocket连接服务端成功

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_spring boot_05

在成功连上服务端后,前端页面刷新可以看到在服务端又重新连接了一次,是因为,前端页面刷新的时候触发了onbeforeunload,理论上来说,页面刷新前进入到了这个onbeforeunload事件后由前端发起关闭,但是实际中我操作的时候不知道为啥,后端没等前端执行webSocket.close();就提前关闭了

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_websocket_06

springboot socket 服务器端和客户端的搭建 springboot websocket 客户端_websocket_07

整体demo

<script type="text/javascript">
        layui.use(['layer'],function () {
                var webSocket = null;
                var layer = layui.layer
                $ = layui.jquery;

                if('WebSocket' in window){
                        webSocket = new WebSocket('ws://felixchan.natapp1.cc/websocket');
                }else {
                        // alert("该浏览器不支持websocket!");
                        layer.msg("该浏览器不支持websocket!",{icon : 5});
                }

                webSocket.onopen = function (event) {
                        console.log('建立连接');
                        webSocket.send("is test connecttions~");
                };

                webSocket.onclose = function (event) {
                        console.log('连接关闭');
                };

                webSocket.onmessage = function (event) {
                        console.log('收到消息:' + event.data);

                        //收到消息之后,可以弹框,播放音乐等
                        //示范一个公告层
                        var audio = document.getElementById('getNewOrderNotice');
                        audio.play();

                        layer.open({
                                type: 1
                                ,title: false //不显示标题栏
                                ,closeBtn: false
                                ,area: '300px;'
                                ,shade: 0.8
                                ,id: 'LAY_layuipro' //设定一个id,防止重复弹出
                                ,btn: ['确定','查看新的订单']
                                ,btnAlign: 'c'
                                ,moveType: 1 //拖拽模式,0或者1
                                ,content: '<div style="padding: 50px; line-height: 22px; background-color: #393D49; color: #fff; font-weight: 300;text-align: center;"><h1>提醒</h1>你有新的订单啦</div>'
                                ,success: function(layero){
                                        var btn = layero.find('.layui-layer-btn');
                                        // 点击查看新订单这里就刷新一下页面
                                        btn.find('.layui-layer-btn1')[0].onclick = function () {
                                                location.reload();//当前页面刷新一下
                                        };
                                }
                        });
                };

                webSocket.onerror = function () {
                        layer.msg("websocket通信发生错误!",{icon : 5});
                };

                // 在窗口关闭的时候把websocket也关闭掉
                window.onbeforeunload = function () {
                        webSocket.close();
                };

        })

</script>

有时候可能后端断了,这个时候前端会同时进入websocket.close()事件中,但是接下来后端重启了,前端不去重连是不行的,所以这里要设置一个定时器,去重连websocket

ps:这里注意重连的时候也是要监听open,close,onmessage这些事件的,即使重新连接上了,后端发消息过来前端也收不到,所以这里可以把监听事件封装成一个方法,把new出来的websocket对象传进去

这里举了个coffee的例子,js也是一样的做法

websocketWithConnected: function(websocket) {
        var it, me;
        it = this.wso;
        me = this;
        websocket.onopen = function() {
          console.log('建立连接');
          return it.rfidPackingCheckwebsocket.send("{pageId:" + it.app.workspaceManager.wsoContainer.selectedChildWidget.wso.navigatorItem.id + "}");
        };
        websocket.onclose = function() {
          return console.log('连接关闭');
        };
        websocket.onmessage = function(event) {
          var data, originalEpcCode, resultEpcCode;
          console.log('收到消息:' + event.data);
          if ('RFIDPacking' === it.app.workspaceManager.wsoContainer.selectedChildWidget.wso.navigatorItem.id) {
            if (it.queryCode && it.queryCode.getValue() !== '') {
              data = JSON.parse(event.data);
              resultEpcCode = data.resultEpcCode;
              originalEpcCode = data.originalEpcCode;
              it.resultEpcCode.innerHTML = resultEpcCode;
              it.originalEpcCode.innerHTML = originalEpcCode;
              it.codeInput.set('value', resultEpcCode);
              if (!me.checkItem(resultEpcCode)) {
                return it.codeInput.set('value', '');
              } else {
                return me.pack(it, me, resultEpcCode);
              }
            } else {
              return layer.alert("请先输入查询code");
            }
          }
        };
        return websocket.onerror = function() {
          return layer.msg("websocket通信发生错误!", {
            icon: 5
          });
        };
      }