1. 程式人生 > >springboot整合websocket

springboot整合websocket

1.pom.xml

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

2.後臺程式碼

package com.cloudtech.web.controller;
 
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.cloudtech.web.dubbo.BaseDataResult;
import com.cloudtech.web.entity.WarnNoticeInfo;
import com.cloudtech.web.util.Constans;
import com.cloudtech.web.util.JacksonUtil;
//@ServerEndpoint("/websocket/{user}")
@Component
@ServerEndpoint(value = "/websocket/{id}/{operatorId}")
public class WebSocketServer{
	
	private static final Logger log = LoggerFactory.getLogger(WarnController.class);
	
    //靜態變數,用來記錄當前線上連線數。應該把它設計成執行緒安全的。
    private static int onlineCount = 0;
    //concurrent包的執行緒安全Set,用來存放每個客戶端對應的MyWebSocket物件。
    static public final ConcurrentMap<Integer, ConcurrentMap<String,WebSocketServer>> webSocketSet = new ConcurrentHashMap<>();
    //使用者集合  key:賬號id   value:賬號明細
    static public final ConcurrentMap<Integer, Integer> principalMaps = new ConcurrentHashMap<>();
    //與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
    private Session session;
    
  
    /**
     * 連線建立成功呼叫的方法
     * @param id    賬號id
     * @param operatorId  運營商id
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("id") Integer id,@PathParam("operatorId") Integer operatorId,Session session) {
        this.session = session;
        
        ConcurrentMap<String, WebSocketServer> concurrentMap = webSocketSet.get(id);
        if(concurrentMap == null){
        	concurrentMap = new ConcurrentHashMap<>(); 
        }
        concurrentMap.put(session.getId(),this);
        webSocketSet.put(id, concurrentMap);
        
        //遺留問題:如果沒有預警許可權可能也會建立連線,這裡暫不考慮
        //按道理應該可以從session中取到對應的值,卡了半天,通過前端傳值
        principalMaps.put(id, operatorId);
        
        addOnlineCount();           //線上數加1
        log.info("有新連線加入!當前線上人數為" + getOnlineCount());
        
        
    }
	//	//連線開啟時執行
	//	@OnOpen
	//	public void onOpen(@PathParam("user") String user, Session session) {
	//		currentUser = user;
	//		System.out.println("Connected ... " + session.getId());
	//	}
 
    /**
     * 連線關閉呼叫的方法
     */
    @OnClose
    public void onClose(@PathParam("id") Integer id,Session session) {
    	ConcurrentMap<String, WebSocketServer> concurrentMap = webSocketSet.get(id);
    	if(concurrentMap != null){
    		concurrentMap.remove(session.getId());
    	}else{
    		webSocketSet.remove(id);
    	}
    	
    	//webSocketSet.remove(this);  //從set中刪除
    	principalMaps.remove(this); 
        subOnlineCount();           //線上數減1
        log.info("有一連線關閉!當前線上人數為" + getOnlineCount());
    }
 
 
	/**
	 * 
	 * @param session
	 * @param error
	 */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤");
        error.printStackTrace();
    }
 
    
    /**
     * 群發訊息
     * @param warnNoticeInfo
     * @throws IOException
     */
    public void sendMessage(WarnNoticeInfo warnNoticeInfo) throws IOException {
    	//轉出json傳送
    	session.getBasicRemote().sendText(JacksonUtil.bean2Json(warnNoticeInfo));
    }
 
    
    /**
     * 群發自定義訊息(系統主動推送)
     * @param noticeInfo
     * @throws IOException
     */
    public BaseDataResult pushBySys(WarnNoticeInfo noticeInfo) throws IOException {
    	log.info(noticeInfo.toString());
    	boolean flag = false;
        for (Entry<Integer, ConcurrentMap<String, WebSocketServer>> item: webSocketSet.entrySet()) {
        	ConcurrentMap<String, WebSocketServer> value = item.getValue();
        	 for (Entry<String, WebSocketServer> socket: value.entrySet()) {
	        	Integer operatorId = principalMaps.get(item.getKey());
	        	//不向該預警下不是對應的運營商明下的使用者推送訊息
	        	if(operatorId == null || operatorId.intValue() != noticeInfo.getOperatorId().intValue()){  //
	        		continue;
	        	}
	            try {
	            	socket.getValue().sendMessage(noticeInfo);
	            } catch (IOException e) {
	            	flag = true;
	                continue;
	            }
        	 }
        }
        
        if(flag){
        	return new BaseDataResult(Constans.FAILED, "推送預警資訊出現異常!");
        }
        return new BaseDataResult(Constans.FAILED, "推送預警資訊成功!");
    }
 
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
 
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }
 
    public static synchronized void subOnlineCount() {
    	if(WebSocketServer.onlineCount > 0){
    		WebSocketServer.onlineCount--;
    	}
    }
}

3.前端程式碼

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<%-- <jsp:include page="../common/head.jsp"></jsp:include> --%>
<link rel="stylesheet" type="text/css"
	href="${pageContext.request.contextPath}/assets/css/layui.css" />
<link rel="stylesheet" type="text/css"
	href="${pageContext.request.contextPath}/assets/css/css.css" />
<script
	src="${pageContext.request.contextPath}/assets/js/jquery-3.2.0.min.js"></script>
<script src="${pageContext.request.contextPath}/assets/js/layui.js"></script>
<script src="${pageContext.request.contextPath}/assets/js/style.js"></script>

<div class="layui-layout layui-layout-admin">
	<div class="layui-header">
	    <!-- 音訊 -->
		<audio  autoplay="autoplay" id="auto" src=""></audio>
		<div class="layui-logo">氣象站資料採集平臺</div>
		<!-- 頭部區域(可配合layui已有的水平導航) -->
		<ul class="layui-nav layui-layout-left">
			<shiro:hasPermission name="admin:base">
				<li class="layui-nav-item"><a href="${pageContext.request.contextPath}/admin/index"  target="list">賬號管理</a></li>
			</shiro:hasPermission>
			<shiro:hasPermission name="station:base">
				<li class="layui-nav-item"><a href="${pageContext.request.contextPath}/station/index"  target="list">站點管理</a></li>
			</shiro:hasPermission>
			<shiro:hasPermission name="warnInfo:base">
				<li class="layui-nav-item"><a href="${pageContext.request.contextPath}/warn/index" 	target="list">預警管理</a></li>
			</shiro:hasPermission>
			<shiro:hasPermission name="realtime:base">
				<li class="layui-nav-item"><a href="">統計分析</a></li>
			</shiro:hasPermission>
			<shiro:hasPermission name="realtime:base">
				<li class="layui-nav-item layui-this"><a href="${pageContext.request.contextPath}/statis/index"  target="list">實時展示</a></li>
		    </shiro:hasPermission>
		    <shiro:hasPermission name="system:base">
		   		 <li class="layui-nav-item"><a href="${pageContext.request.contextPath}/system/index"  target="list">系統管理</a></li>
			 </shiro:hasPermission>
		</ul>

		<ul class="layui-nav layui-layout-right">
			<li class="layui-nav-item"><a href="javascript:;"> <img
					src="${pageContext.request.contextPath}/images/common/userIcon.jpg" class="layui-nav-img"> ${principal.name}
			</a> <!-- <dl class="layui-nav-child">
                        <dd>
                            <a href="">基本資料</a>
                        </dd>
                        <dd>
                            <a href="">安全設定</a>
                        </dd>
                    </dl> --></li>
			<li class="layui-nav-item"><a onClick="self.parent.location = '${pageContext.request.contextPath}/index/loginout';">登出</a>
			</li>
		</ul>
	</div>
</div>
	<script>
		layui.use('element', function() {
			var element = layui.element;
		});
		$("#changeP").click(function() {
			window.parent.$("#rigth")[0].contentWindow.passwordC();
		})
	</script>
	<script type="text/javascript">
    var websocket = null;
    //判斷當前瀏覽器是否支援WebSocket
    var id = ${principal.id};
    var operatorId = ${principal.operatorId};
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://192.168.1.161:8080/climate/websocket/"+id+"/"+operatorId);
    }
    else {
        alert('當前瀏覽器 Not support websocket')
    }

    //連線發生錯誤的回撥方法
    websocket.onerror = function () {
        setMessageInnerHTML("WebSocket連線發生錯誤");
    };

    //連線成功建立的回撥方法
    websocket.onopen = function () {
        setMessageInnerHTML("WebSocket連線成功");
    }

    //接收到訊息的回撥方法
    websocket.onmessage = function (event) {
    	if(event.data != null){
    		//音訊
            var auto = $("#auto");
            auto.attr("src",'../assets/audio/6124.wav');              
    	}
    	var obj = JSON.parse(event.data);
    	console.log("test:"+obj.id);
        setMessageInnerHTML(event.data);
    }

    //連線關閉的回撥方法
    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(message);
    }
</script>
</head>

        <!-- 音訊 -->
        <audio  autoplay="autoplay" id="auto" src=""></audio>  

package com.cloudtech.web.config;

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();
    }
 
}

上面這句話是後臺推送後,聲音提醒

websocket坑點1:ws://192.168.1.161:8080/climate/websocket

專案訪問的地址需要跟websocket一樣,不然會有問題