最近由于项目需要实时显示数据库更新的数据变化情况,一开始想过在前端使用ajax异步轮询方法实现,但后面考虑到性能和流量等要求,就放弃该方法而选择使用websocket(毕竟现在springboot整合websocket的技术算是比较成熟了,哈哈),现在此小小记录下。
首先,在springboot项目创建并配置成功的基础上对websocket进行整合。
1、在pom文件中添加对websocket的依赖
1 <!-- 引入 websocket 依赖类--> 2 <dependency> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-websocket</artifactId> 5 </dependency>
2、个人习惯是先从后端写到前端,先写websocket的配置类吧
1 import org.springframework.stereotype.Component; 2 3 import javax.websocket.*; 4 import javax.websocket.server.ServerEndpoint; 5 import java.io.IOException; 6 import java.util.concurrent.CopyOnWriteArraySet; 7 8 /** 9 * @Name:WebSocket 10 * @Description:WebSocket配置 11 * @Version:V1.0.0 12 * @Author:mYunYu 13 * @Create Date:2018/11/15 14:45 14 */ 15 @Component 16 @ServerEndpoint(value = "/ws/webSocket" , encoders = {EncoderClassVo.class}) 17 public class WebSocket { 18 //每个客户端都会有相应的session,服务端可以发送相关消息 19 private Session session; 20 21 //J.U.C包下线程安全的类,主要用来存放每个客户端对应的webSocket连接 22 private static CopyOnWriteArraySet<WebSocket> copyOnWriteArraySet = new CopyOnWriteArraySet<WebSocket>(); 23 24 /** 25 * @Name:onOpen 26 * @Description:打开连接。进入页面后会自动发请求到此进行连接 27 * @Author:mYunYu 28 * @Create Date:14:46 2018/11/15 29 * @Parameters: 30 * @Return: 31 */ 32 @OnOpen 33 public void onOpen(Session session) { 34 this.session = session; 35 copyOnWriteArraySet.add(this); 36 System.out.println("websocket有新的连接, 总数:"+ copyOnWriteArraySet.size()); 37 38 } 39 40 /** 41 * @Name:onClose 42 * @Description:用户关闭页面,即关闭连接 43 * @Author:mYunYu 44 * @Create Date:14:46 2018/11/15 45 * @Parameters: 46 * @Return: 47 */ 48 @OnClose 49 public void onClose() { 50 copyOnWriteArraySet.remove(this); 51 System.out.println("websocket连接断开, 总数:"+ copyOnWriteArraySet.size()); 52 } 53 54 /** 55 * @Name:onMessage 56 * @Description:测试客户端发送消息,测试是否联通 57 * @Author:mYunYu 58 * @Create Date:14:46 2018/11/15 59 * @Parameters: 60 * @Return: 61 */ 62 @OnMessage 63 public void onMessage(String message) { 64 System.out.println("websocket收到客户端发来的消息:"+message); 65 } 66 67 /** 68 * @Name:onError 69 * @Description:出现错误 70 * @Author:mYunYu 71 * @Create Date:14:46 2018/11/15 72 * @Parameters: 73 * @Return: 74 */ 75 @OnError 76 public void onError(Session session, Throwable error) { 77 System.out.println("发生错误:" + error.getMessage() + "; sessionId:" + session.getId()); 78 error.printStackTrace(); 79 } 80 81 public void sendMessage(Object object){ 82 //遍历客户端 83 for (WebSocket webSocket : copyOnWriteArraySet) { 84 System.out.println("websocket广播消息:" + object.toString()); 85 try { 86 //服务器主动推送 87 webSocket.session.getBasicRemote().sendObject(object) ; 88 } catch (Exception e) { 89 e.printStackTrace(); 90 } 91 } 92 } 93 94 /** 95 * @Name:sendMessage 96 * @Description:用于发送给客户端消息(群发) 97 * @Author:mYunYu 98 * @Create Date:14:46 2018/11/15 99 * @Parameters: 100 * @Return: 101 */ 102 public void sendMessage(String message) { 103 //遍历客户端 104 for (WebSocket webSocket : copyOnWriteArraySet) { 105 System.out.println("websocket广播消息:" + message); 106 try { 107 //服务器主动推送 108 webSocket.session.getBasicRemote().sendText(message); 109 } catch (Exception e) { 110 e.printStackTrace(); 111 } 112 } 113 } 114 115 /** 116 * @Name:sendMessage 117 * @Description:用于发送给指定客户端消息 118 * @Author:mYunYu 119 * @Create Date:14:47 2018/11/15 120 * @Parameters: 121 * @Return: 122 */ 123 public void sendMessage(String sessionId, String message) throws IOException { 124 Session session = null; 125 WebSocket tempWebSocket = null; 126 for (WebSocket webSocket : copyOnWriteArraySet) { 127 if (webSocket.session.getId().equals(sessionId)) { 128 tempWebSocket = webSocket; 129 session = webSocket.session; 130 break; 131 } 132 } 133 if (session != null) { 134 tempWebSocket.session.getBasicRemote().sendText(message); 135 } else { 136 System.out.println("没有找到你指定ID的会话:{}"+ "; sessionId:" + sessionId); 137 } 138 } 139 140 /** 141 * 如果使用springboot内置tomcat,需要配置,否则不需要 142 * 143 * @return 144 */ 145 // @Bean 146 // public ServerEndpointExporter serverEndpointExporter() { 147 // return new ServerEndpointExporter(); 148 // } 149 150 151 }
上面类中的红色注释的代码是只有当springboot项目使用内置tomcat时需要配置(即打成jar包),如果使用外置tomcat则不需要配置(即打成war包)
3、配置编码器,主要是需要后端向前端发送对象数据,如果只是发送普通的字符串数据的话,就不需要
1 import com.alibaba.fastjson.JSON; 2 import com.xxx.utils.MsgUtil; 3 4 import javax.websocket.EncodeException; 5 import javax.websocket.Encoder; 6 import javax.websocket.EndpointConfig; 7 8 /** 9 * @Name:EncoderClassVo 10 * @Description:编码器,防止发送对象出错 11 * @Version:V1.0.0 12 * @Author:mYunYu 13 * @Create Date:2018/11/15 14:43 14 */ 15 public class EncoderClassVo implements Encoder.Text<MsgUtil> { 16 17 @Override 18 public void init(EndpointConfig endpointConfig) { 19 20 } 21 22 @Override 23 public void destroy() { 24 25 } 26 27 @Override 28 public String encode(MsgUtil msgUtil) throws EncodeException { 29 try{ 30 return JSON.toJSONString(msgUtil) ; 31 }catch (Exception e){ 32 e.printStackTrace() ; 33 return null; 34 } 35 } 36 }
4、接下来就是写controller层了,我这边demo只是简单的发送一些随机的数据,具体信息还需要根据各个项目需要来写
1 import com.xxx.pojo.User; 2 import com.xxx.utils.MsgUtil; 3 import com.xxx.webSocket.WebSocket; 4 import org.springframework.web.bind.annotation.RequestMapping; 5 import org.springframework.web.bind.annotation.RestController; 6 7 import javax.annotation.Resource; 8 import java.io.IOException; 9 10 /** 11 * @Name:SocketController 12 * @Description:消息发送Controller 13 * @Version:V1.0.0 14 * @Author:mYunYu 15 * @Create Date:2018/11/15 16:44 16 */ 17 @RestController 18 public class SocketController { 19 20 @Resource 21 WebSocket webSocket; 22 23 /** 24 * @Name:helloManyWebSocket 25 * @Description:群发消息 26 * @Author:mYunYu 27 * @Create Date:16:44 2018/11/15 28 * @Parameters: 29 * @Return: 30 */ 31 @RequestMapping("many") 32 public String helloManyWebSocket(){ 33 34 int i = 1 ; 35 while(i > 0){ 36 i=1+(int)(Math.random()*600) ; 37 User user = new User() ; 38 user.setUserid(i+1) ; 39 user.setUsername(String.valueOf(i) + String.valueOf(i+i)) ; 40 41 //将对象转为json对象,并发送到前端 42 MsgUtil msgUtil = MsgUtil.success().addMsg("map", user); 43 webSocket.sendMessage(msgUtil); 44 45 try{ 46 Thread.sleep(1000) ; 47 }catch (Exception e){ 48 e.printStackTrace() ; 49 } 50 } 51 52 return "发送成功"; 53 } 54 55 /** 56 * @Name:helloOneWebSocket 57 * @Description:根据session单个发送消息 58 * @Author:mYunYu 59 * @Create Date:16:44 2018/11/15 60 * @Parameters: 61 * @Return: 62 */ 63 @RequestMapping("one") 64 public String helloOneWebSocket(String sessionId) throws IOException { 65 //向某个人发送消息 66 webSocket.sendMessage(sessionId,"你好~!,单个用户"); 67 68 return "发送成功"; 69 } 70 71 72 }
5、接下来该在前端写配置了,我这里使用的是thymeleaf模板
1 <!DOCTYPE html > 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta charset="UTF-8"/> 5 <title>Title</title> 6 </head> 7 <body> 8 9 自动连接websocket 10 <script type="text/javascript"> 11 12 var socket; 13 if (typeof (WebSocket) == "undefined") { 14 console.log("遗憾:您的浏览器不支持WebSocket"); 15 } else { 16 console.log("恭喜:您的浏览器支持WebSocket"); 17 18 //实现化WebSocket对象 19 //指定要连接的服务器地址与端口建立连接 20 //注意ws、wss使用不同的端口。我使用自签名的证书测试, 21 //无法使用wss,浏览器打开WebSocket时报错 22 //ws对应http、wss对应https。 23 socket = new WebSocket("ws://127.0.0.1:端口号/项目名/ws/webSocket"); 24 //连接打开事件 25 socket.onopen = function() { 26 console.log("Socket 已打开"); 27 socket.send("消息发送测试(From Client)"); 28 }; 29 //收到消息事件 30 socket.onmessage = function(msg) { 31 console.log(msg.data) ; 32 }; 33 //连接关闭事件 34 socket.onclose = function() { 35 console.log("Socket已关闭"); 36 }; 37 //发生了错误事件 38 socket.onerror = function() { 39 alert("Socket发生了错误"); 40 } 41 42 //窗口关闭时,关闭连接 43 window.unload=function() { 44 socket.close(); 45 }; 46 47 } 48 49 </script> 50 51 52 </body> 53 </html>
6、最后就是测试了,先右击项目,选择Run Mavan-->clear install进行项目打包
打包成功之后再启动外置tomcat来启动项目
启动项目成功,在浏览器中访问
按f12,如果出现”Socket已打开“字样,则表示客户端连接ok,现在需要使用服务端向客户端发送消息
再打开另一个浏览器,输入http://localhost:端口号/项目名/many进行访问,就可以观察到idea和浏览器中已经打印了发送的消息字样了
上图是客户端访问的浏览器所显示的消息,下图是服务器端所打印的消息
好了,实现的很成功,接下来就是对发送的数据进行替换就差不多了,哈哈~