更多文章欢送关注公众号: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 + " "); } </script></html>1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
6.后果展现
后盾:
如果有连贯申请
前台显示:
总结
这两头我遇到一个问题,就是说WebSocket启动的时候优先于spring容器,从而导致在WebSocketServer中调用业务Service会报空指针异样
所以须要在WebSocketServer中将所须要用到的service给动态初始化一下:
如图所示:
还须要做如下配置:
作者:大树学生https://blog.csdn.net/MacWx/a...