springboot - websocket实现及原理

本文章包括websocket面试相关问题以及spring boot如何整合webSocket。javascript

参考文档 https://blog.csdn.net/prayallforyou/article/details/53737901 、https://www.cnblogs.com/bianzy/p/5822426.htmlcss

  webSocket是HTML5的一种新协议,它实现了服务端与客户端的全双工通讯,创建在传输层,tcp协议之上,即浏览器与服务端须要先创建tcp协议,再发送webSocket链接创建请求。html

  webSocket的链接:客户端发送请求信息,服务端接受到请求并返回相应的信息。链接创建。客户端发送http请求时,经过  Upgrade:webSocket Connection:Upgrade 告知服务器须要创建的是webSocket链接,而且还会传递webSocket版本号,协议的字版本号,原始地址,主机地址等等。前端

  webSocket相互通讯的Header很小,大概只有2Bytes。java


  如下是基于spring boot及支持webScoket的高版本浏览器的配置过程。git

1、pom.xml中引入webSocket组件github

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

2、后台引入webSocketweb

  要想让一个类处理webScoket的请求,须要两个东西:面试

  ①  类名上加webScoket请求拦截注释@ServerEndpoint(value="/***")spring

    类须要继承org.springframework.web.socket.server.standard.ServerEndpointExporter,重写交互过程当中各类状况下调用的方法(创建时、断开时、出错时、接收消息、发送消息)

  针对②,一方面根据spring的IOC特性,须要反向代理,另外一方面由于webSocket是一个功能而不单单是属于某个业务,因此应当在配置文件中声明。配置文件能够是xml文件,也能够是注释了@Configuration的类文件,根据我的喜爱使用~,这里使用的是@Configuration。

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

  使用@ServerEndpoint(value="/***") 时会自动注入返回类型为ServerEndpointExporter的bean。等同于继承了ServerEndpointExporter类。继承类后再重写onOpen、onClose、onMessage、onError方法,由于不是直接使用继承,因此方法的重写也须要使用注释,代码以下

package com.example.SpringBootTry.controller.webSocket; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; /** * 双工通讯websocket工具类 * @author wwl * */ @ServerEndpoint(value="/webSocket") @Component public class WebSocketUtil{ //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。
    private static int onlineCount = 0; //concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<WebSocketUtil>(); //与某个客户端的链接会话,须要经过它来给客户端发送数据
    private Session session; /** * 链接创建成功调用的方法*/ @OnOpen public void onOpen(Session session) { this.session = session; webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新链接加入!当前在线人数为" + getOnlineCount()); try { sendMessage("您是第" + getOnlineCount() + "个双工通讯的用户!"); } catch (IOException e) { System.out.println("IO异常"); } } /** * 链接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this);  //从set中删除
        subOnlineCount();           //在线数减1
        System.out.println("有一链接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息*/ @OnMessage public void onMessage(String message, Session session) { System.out.println("来自客户端的消息:" + message); //发送消息
        try { session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } } /** * 发生错误时调用 */ @OnError public void onError(Session session, Throwable error) { System.out.println("发生错误"); error.printStackTrace(); } /** * 发送消息 * @param message * @throws IOException */
    public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); //this.session.getAsyncRemote().sendText(message);
 } /** * 群发自定义消息 * */
    public static void sendInfo(String message) throws IOException { for (WebSocketUtil item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketUtil.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketUtil.onlineCount--; } }

 3、前端引入webSocket

  使用  var websocket = new WebSocket("ws://localhost:8081/***")  创建webSocket链接,定义websocket的onerror、onopen、onmessage、onclose的属性,跟后台的四个方法相对应,完成合理的webSocket交互。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
    <title>websocket测试页面</title>    
    <meta http-equiv="keywords" content="websocket,例子">
    <meta http-equiv="description" content="测试websocket">
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 
    <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body> Welcome<br/>
    <input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
    <div id="message">
    </div>    
</body>
<script type="text/javascript">
    var websocket = null; //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){ websocket = new WebSocket("ws://localhost:8081/webSocket"); }else{ alert('Not support websocket') } //链接发生错误的回调方法
 websocket.onerror = function(){ setMessageInnerHTML("error"); }; //链接成功创建的回调方法
 websocket.onopen = function(event){ setMessageInnerHTML("open"); } //接收到消息的回调方法
 websocket.onmessage = function(event){ setMessageInnerHTML(event.data); } //链接关闭的回调方法
 websocket.onclose = function(){ setMessageInnerHTML("close"); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket链接,防止链接还没断开就关闭窗口,server端会抛异常。
 window.onbeforeunload = function(){ websocket.close(); } //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){ document.getElementById('message').innerHTML += innerHTML + '<br/>'; } //关闭链接
    function closeWebSocket(){ websocket.close(); } //发送消息
    function send(){ var message = document.getElementById('text').value; websocket.send(message); } </script>
</html>

以上为spring boot 整合webSocket的一些入门知识。有错误欢迎指正。

demo地址:https://github.com/ttjsndx/someDemo/blob/master/SpringBootTryDemo.rar

相关文章
相关标签/搜索