Spring Boot 之Websocket 编程笔记

    有时候会用到向前端页推送消息的状况,这时候就会用到WebSocket 编程了,javascript

1.在pom.xml 中添加以下配置html

  1.1核心是@ServerEndpoint这个注解。这个注解是Javaee标准里的注解,tomcat7以上已经对其进行了实现,若是是用传统方法使用tomcat发布项目,只要在pom文件中引入javaee标准便可使用前端

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

  1.2 但使用springboot的内置tomcat时,就不须要引入javaee-api了,spring-boot已经包含了。使用springboot的websocket功能首先引入springboot组件。java

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>1.3.5.RELEASE</version>
        </dependency>

          1.3. springboot的高级组件会自动引用基础的组件,像spring-boot-starter-websocket就引入了spring-boot-starter-web和spring-boot-starter,因此不要重复引入。web

2. 使用@ServerEndpoint 创立 websocket endpointspring

  首先要注入ServerEndointExporTer,这个bean 会自动注册使用了@ServerEndpoint 注解声明的Websocket endpoint.要注意,若是使用独立的servlet容器,而不是直接使用springBoot 内置容器,就不要注入ServerEndpointExporter,由于它将由容器本身提供和管理。编程

配置类的代码以下:api

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {

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

3. 写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.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.springframework.stereotype.Component;

@ServerEndpoint(value = "/websocket/{userCode}")
@Component
public class MyWebSocket {

	 //静态变量,用来记录当前在线链接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    //concurrent包的线程安全Set,用来存放每一个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
   
    // 与某个客户的连接会话,须要经过它来给客户端发送数据
    
    private String currentUser;
    
    //与某个客户端的链接会话,须要经过它来给客户端发送数据
    private Session session;

    /**
     * 链接创建成功调用的方法*/
    @OnOpen
    public void onOpen(@PathParam("userCode") String userCode ,Session session) {
        this.session = session;
        this.currentUser = userCode;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在线数加1
        System.out.println("有新链接加入!当前在线人数为" + getOnlineCount());
        try {
            sendMessage("ok");
        } 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);

        //群发消息
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }


    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static void sendMessageTo(String message,String userCode) throws IOException{
    	for(MyWebSocket item :webSocketSet){
    		if(item.currentUser.equals(userCode)){
    			item.session.getBasicRemote().sendText(message);
    		}
    	}
    }

    /**
     * 群发自定义消息
     * */
    public static void sendInfo(String message) throws IOException {
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

使用springboot的惟一区别是要@Component声明下,而使用独立容器是由容器本身管理websocket的,但在springboot中连容器都是spring管理的。tomcat

虽然@Component默认是单例模式的,但springboot仍是会为每一个websocket链接初始化一个bean,因此能够用一个静态set保存起来。

4.页面调用简单的方法类

<html>
    <head>
        <title> My Websocket</title>
    </head>
    <body>
        Wecome <br/>
        <input id="text"/><button onclick="send()">Send</button>
        <button onclick="closeWebSocket()">Close</button>
        <div id="message">

        </div>
        <script type="text/javascript">
        
         var websocket = null;
         //判断当前浏览器是否支持webSocket
         if('WebSocket' in window){
           websocket = new WebSocket("ws://127.0.0.1:8988/websocket/1");
         }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");
        }

        //监听窗口关闭事件,当窗口关闭时,主动关闭wesocket 连接,防止连接还没断开就关闭窗口,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>

    </body>
</html>

 四、总结

  springboot已经作了深度的集成和优化,要注意是否添加了不须要的依赖、配置或声明。因为不少讲解组件使用的文章是和spring集成的,会有一些配置,在使用springboot时,因为springboot已经有了本身的配置,再这些配置有可能致使各类各样的异常。

 在webSocket 类中的参数不能引入实体类,这样回报错误,具体为啥报错误,我也没有找到缘由,回头研究清楚了再补充上

相关文章
相关标签/搜索