项目中经常会用到消息推送功能,关于推送技术的实现,我们通常会联想到轮询、comet长连接技术,虽然这些技术能够实现,但是需要反复连接,对于服务资源消耗过大,随着技术的发展,HtML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。本文将介绍如何采用websocket实现消息推送。
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。浏览器和服务器仅需一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
Websocket协议基于Http协议,针对Http协议进行了相关的改善,且Websocket协议也需要建立TCP连接来实现数据传输,具体实现如下图:

说明:
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.4.RELEASE</version>
- <relativePath/>
- </parent>
-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-websocket</artifactId>
- </dependency>
-
- 复制代码
- @Configuration
- public class WebSocketConfig
- {
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
- return new ServerEndpointExporter();
- }
- }
-
- 复制代码
- @ServerEndpoint(value="/websocket/{uid}")
- @Component
- public class WebSocketServer
- {
- private Logger logger = LoggerFactory.getLogger(WebSocketServer.class);
-
- private static final AtomicInteger onlineCount = new AtomicInteger(0);
-
- private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>();
-
- @OnOpen
- public void onOpen(Session session,@PathParam("uid") String uid)
- {
- logger.info("open message uid:{}",uid);
- sessionSet.add(session);
- onlineCount.incrementAndGet();
- logger.info("窗口开始监听uid:{},当前在线人数:{}",uid,onlineCount);
- }
-
- @OnClose
- public void onClose(Session session)
- {
- String sessionId=session.getId();
- logger.info("sessionid:{} close",sessionId);
- sessionSet.remove(this);
- int count=onlineCount.decrementAndGet();
- logger.info("有一连接关闭!当前在线人数为:{}",count);
- }
-
- @OnError
- public void onError(Session session, Throwable error)
- {
- logger.error("消息发生错误:{},Session ID: {}",error.getMessage(),session.getId());
- }
-
-
- public void batchSendMesasge(String uid,String message) throws IOException
- {
- logger.info("推送消息到窗口:{},推送内容:{}",uid,message);
- for(Session session:sessionSet){
- sendMessage(session, message);
- }
- }
-
- public void sendMessage(Session session, String message) throws IOException {
-
- if(session!=null)
- {
- synchronized (session) {
- session.getBasicRemote().sendText(message);
- }
- }
- }
-
- }
- 复制代码
说明: @OnOpen :当有新的WebSocket连接进入时调用 @OnClose:当有WebSocket连接关闭时调用 @OnError :当有WebSocket抛出异常时调用 @OnMessage:当接收到字符串消息时,对该方法进行回调
- @Controller
- public class WebScoketController
- {
- @Autowired
- private WebSocketServer webSocketServer;
-
- @ResponseBody
- @RequestMapping("/sendMessage")
- public String batchMessage(String uid,String message)
- {
- Map<String, String> map =new HashMap<String, String>();
- try
- {
- map.put("code", "200");
- webSocketServer.batchSendMesasge(uid,message);
- }
- catch (Exception e)
- {
- map.put("code", "-1");
- map.put("message", e.getMessage());
- }
- return JSON.toJSONString(map);
- }
-
- @GetMapping("/enter")
- public String enter()
- {
- return "webscoketTest.html";
- }
- }
-
- 复制代码
- <!DOCTYPE HTML>
- <html>
- <head>
- <meta charset="utf-8">
- <title>websocket test</title>
-
- <script type="text/javascript">
- if ("WebSocket" in window)
- {
- console.log("您的浏览器支持 WebSocket!");
-
- var ws = new WebSocket("ws://127.0.0.1:9092/websocket/1234");
- console.log('ws连接状态:' + ws.readyState);
-
- //打开
- ws.onopen = function()
- {
- ws.send("message test");
- console.log("mesage sending");
- };
-
- //发送消息
- ws.onmessage = function (evt)
- {
- var received_msg = evt.data;
- alert(received_msg);
- };
-
- //关闭
- ws.onclose = function()
- {
- // 关闭 websocket
- console.log("socket is close");
- };
- }
-
- else
- {
- console.log("您的浏览器不支持 WebSocket!");
- }
- </script>
- </head>
- </html>
- 复制代码
执行的结果如下:
- open message uid:1234
- [nio-9092-exec-2] c.s.f.w.controller.WebSocketServer: 窗口开始监听uid:1234,当前在线人数:1
- [nio-9092-exec-5] c.s.f.w.controller.WebSocketServer: 推送消息到窗口:1234,推送内容:this is message
- 复制代码
本文对于websocket进行简单讲解,关于其他的高级功能将在后续文章中进行说明,如有疑问,请随时反馈。