更多文章欢送关注公众号:Java版web我的项目,外面还蕴含了从根底到进阶的各种材料

前言

在一次我的项目开发中,应用到了Netty网络应用框架,以及MQTT进行音讯数据的收发,这其中须要后盾来将获取到的音讯被动推送给前端,于是就应用到了MQTT,特此记录一下。

一、什么是websocket?

WebSocket协定是基于TCP的一种新的网络协议。它实现了客户端与服务器全双工通信,学过计算机网络都晓得,既然是全双工,就阐明了服务器能够被动发送信息给客户端。这与咱们的推送技术或者是多人在线聊天的性能不约而同。

为什么不应用HTTP 协定呢?这是因为HTTP是单工通信,通信只能由客户端发动,客户端申请一下,服务器解决一下,这就太麻烦了。于是websocket应运而生。

上面咱们就间接开始应用Springboot开始整合。以下案例都在我本人的电脑上测试胜利,你能够依据本人的性能进行批改即可。
我的我的项目构造如下:

二、应用步骤

1.增加依赖

Maven依赖:

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

2.启用Springboot对WebSocket的反对

启用WebSocket的反对也是很简略,几句代码搞定:

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/** * @ Auther: 马超伟 * @ Date: 2020/06/16/14:35 * @ Description: 开启WebSocket反对 */@Configurationpublic class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }}123456789101112131415

3.外围配置:WebSocketServer

因为WebSocket是相似客户端服务端的模式(采纳ws协定),那么这里的WebSocketServer其实就相当于一个ws协定的Controller

  • @ ServerEndpoint 注解是一个类档次的注解,它的性能次要是将目前的类定义成一个websocket服务器端, 注解的值将被用于监听用户连贯的终端拜访URL地址,客户端能够通过这个URL来连贯到WebSocket服务器端
  • 新建一个ConcurrentHashMap webSocketMap 用于接管以后userId的WebSocket,不便传递之间对userId进行推送音讯。

上面是具体业务代码:

package cc.mrbird.febs.external.webScoket;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.time.LocalDateTime;import java.util.List;import java.util.concurrent.CopyOnWriteArraySet;/** * Created with IntelliJ IDEA. * @ Auther: 马超伟 * @ Date: 2020/06/16/14:35 * @ Description: * @ ServerEndpoint 注解是一个类档次的注解,它的性能次要是将目前的类定义成一个websocket服务器端, * 注解的值将被用于监听用户连贯的终端拜访URL地址,客户端能够通过这个URL来连贯到WebSocket服务器端 */@Component@Slf4j@Service@ServerEndpoint("/api/websocket/{sid}")public class WebSocketServer {    //动态变量,用来记录以后在线连接数。应该把它设计成线程平安的。    private static int onlineCount = 0;    //concurrent包的线程平安Set,用来寄存每个客户端对应的MyWebSocket对象。    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();    //与某个客户端的连贯会话,须要通过它来给客户端发送数据    private Session session;    //接管sid    private String sid = "";    /**     * 连贯建设胜利调用的办法     */    @OnOpen    public void onOpen(Session session, @PathParam("sid") String sid) {        this.session = session;        webSocketSet.add(this);     //退出set中        this.sid = sid;        addOnlineCount();           //在线数加1        try {            sendMessage("conn_success");            log.info("有新窗口开始监听:" + sid + ",以后在线人数为:" + getOnlineCount());        } catch (IOException e) {            log.error("websocket IO Exception");        }    }    /**     * 连贯敞开调用的办法     */    @OnClose    public void onClose() {        webSocketSet.remove(this);  //从set中删除        subOnlineCount();           //在线数减1        //断开连接状况下,更新主板占用状况为开释        log.info("开释的sid为:"+sid);        //这里写你 开释的时候,要解决的业务        log.info("有一连贯敞开!以后在线人数为" + getOnlineCount());    }    /**     * 收到客户端音讯后调用的办法     * @ Param message 客户端发送过去的音讯     */    @OnMessage    public void onMessage(String message, Session session) {        log.info("收到来自窗口" + sid + "的信息:" + message);        //群发音讯        for (WebSocketServer item : webSocketSet) {            try {                item.sendMessage(message);            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * @ Param session     * @ Param error     */    @OnError    public void onError(Session session, Throwable error) {        log.error("产生谬误");        error.printStackTrace();    }    /**     * 实现服务器被动推送     */    public void sendMessage(String message) throws IOException {        this.session.getBasicRemote().sendText(message);    }    /**     * 群发自定义音讯     */    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {        log.info("推送音讯到窗口" + sid + ",推送内容:" + message);        for (WebSocketServer item : webSocketSet) {            try {                //这里能够设定只推送给这个sid的,为null则全副推送                if (sid == null) {//                    item.sendMessage(message);                } else if (item.sid.equals(sid)) {                    item.sendMessage(message);                }            } catch (IOException e) {                continue;            }        }    }    public static synchronized int getOnlineCount() {        return onlineCount;    }    public static synchronized void addOnlineCount() {        WebSocketServer.onlineCount++;    }    public static synchronized void subOnlineCount() {        WebSocketServer.onlineCount--;    }    public static CopyOnWriteArraySet<WebSocketServer> getWebSocketSet() {        return webSocketSet;    }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141

4.测试Controller

import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.ModelAndView;import java.io.IOException;import java.util.HashMap;import java.util.Map;/** * Created with IntelliJ IDEA. * * @ Auther: 马超伟 * @ Date: 2020/06/16/14:38 * @ Description: */@Controller("web_Scoket_system")@RequestMapping("/api/socket")public class SystemController {    //页面申请    @GetMapping("/index/{userId}")    public ModelAndView socket(@PathVariable String userId) {        ModelAndView mav = new ModelAndView("/socket1");        mav.addObject("userId", userId);        return mav;    }    //推送数据接口    @ResponseBody    @RequestMapping("/socket/push/{cid}")    public Map pushToWeb(@PathVariable String cid, String message) {        Map<String,Object> result = new HashMap<>();        try {            WebSocketServer.sendInfo(message, cid);            result.put("code", cid);            result.put("msg", message);        } catch (IOException e) {            e.printStackTrace();        }        return result;    }}12345678910111213141516171819202122232425262728293031323334353637383940414243444546

5.测试页面index.html

<!DOCTYPE html><html>    <head>        <meta charset="utf-8">        <title>Java后端WebSocket的Tomcat实现</title>        <script type="text/javascript" src="js/jquery.min.js"></script>    </head>    <body>        <div id="main" style="width: 1200px;height:800px;"></div>        Welcome<br/><input id="text" type="text" />        <button onclick="send()">发送音讯</button>        <hr/>        <button onclick="closeWebSocket()">敞开WebSocket连贯</button>        <hr/>        <div id="message"></div>    </body>    <script type="text/javascript">        var websocket = null;        //判断以后浏览器是否反对WebSocket        if('WebSocket' in window) {            //改成你的地址            websocket = new WebSocket("ws://192.168.100.196:8082/api/websocket/100");        } else {            alert('以后浏览器 Not support websocket')        }        //连贯产生谬误的回调办法        websocket.onerror = function() {            setMessageInnerHTML("WebSocket连贯产生谬误");        };        //连贯胜利建设的回调办法        websocket.onopen = function() {            setMessageInnerHTML("WebSocket连贯胜利");        }        var U01data, Uidata, Usdata        //接管到音讯的回调办法        websocket.onmessage = function(event) {            console.log(event);            setMessageInnerHTML(event);            setechart()        }        //连贯敞开的回调办法        websocket.onclose = function() {            setMessageInnerHTML("WebSocket连贯敞开");        }        //监听窗口敞开事件,当窗口敞开时,被动去敞开websocket连贯,避免连贯还没断开就敞开窗口,server端会抛异样。        window.onbeforeunload = function() {            closeWebSocket();        }        //将音讯显示在网页上        function setMessageInnerHTML(innerHTML) {            document.getElementById('message').innerHTML += innerHTML + '<br/>';        }        //敞开WebSocket连贯        function closeWebSocket() {            websocket.close();        }        //发送音讯        function send() {            var message = document.getElementById('text').value;            websocket.send('{"msg":"' + message + '"}');            setMessageInnerHTML(message + "&#13;");        }    </script></html>1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374

6.后果展现

后盾:
如果有连贯申请

前台显示:

总结

这两头我遇到一个问题,就是说WebSocket启动的时候优先于spring容器,从而导致在WebSocketServer中调用业务Service会报空指针异样

所以须要在WebSocketServer中将所须要用到的service给动态初始化一下:
如图所示:

还须要做如下配置:

作者:大树学生

https://blog.csdn.net/MacWx/a...