1. 程式人生 > 其它 >SSM框架整合WebSocket實現訊息推送長連線,WebSocket實現掃碼登入

SSM框架整合WebSocket實現訊息推送長連線,WebSocket實現掃碼登入

技術標籤:前端後端前後端同效果測試websocketjavassmhtml5javascript

使用SSM框架整合WebSocket

pom.xml中引入依賴

<!-- WebSocket配置-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>${spring.version}</version>
</dependency
>
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency>

Java中
複製過去稍微改一下自己需要的東西就好了,這裡的‘大資料’推送可以不管
不用在xml中配置也可以直接使用,使用時記得把過濾器的攔截放行‘/webSocket’請求

package
com.websocket; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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 net.sf.json.JSONObject; @ServerEndpoint("/webSocket/{type}/{uuid}") public class WebSocket { private static int onlineCount = 0; //本來用統計線上人數的 private static Map<String, WebSocket> loginMap = new ConcurrentHashMap<String, WebSocket>(); // 餓漢式 private static List<Map<String, WebSocket>> clients = null; // 懶漢式 public Session session; public String userName; public String uuid; public static long timeCount = 0; /** * 開啟連線 * @param type * @param username * @param session * @throws IOException */ @OnOpen public void onOpen(@PathParam("type") String type,@PathParam("uuid") String uuid, Session session) throws IOException { this.userName = type; this.uuid = uuid; this.session = session; if(type != null) { if(type.equals("login")) {// 登入 loginMap.put(uuid, this); }else{ if(clients == null) {// 大資料 synchronized (WebSocket.class) { if(clients == null) { clients = new ArrayList<Map<String, WebSocket>>(); } } } Map<String, WebSocket> clien = new ConcurrentHashMap<String, WebSocket>(); clien.put(uuid, this); clients.add(clien); } } //addOnlineCount();//本來用統計線上人數的 } /** * 刪除 * @throws IOException */ @OnClose public void onClose() throws IOException { if(userName != null && userName.equals("login")) {// 登入 if(loginMap != null) { loginMap.remove(uuid); } }else { if(clients != null) { for(int i=0; i< clients.size(); i++) { // 大資料 Map<String, WebSocket> clien = clients.get(i); for (WebSocket item : clien.values()) { if(item.session.equals(session)) clients.remove(i); } } } } //subOnlineCount(); //本來用統計線上人數的 } /** * 錯誤資訊 */ @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } /** * 接收資料 * @param jsonTo * @throws IOException */ @OnMessage public static void onMessage(String message) throws IOException { JSONObject jsonTo = JSONObject.fromObject(message); String mes = (String) jsonTo.get("message"); String type = (String) jsonTo.get("type"); String to = (String) jsonTo.get("To"); if(type != null) { if(type.equals("login")){// 登入 sendMessageLoginTo(mes, to); }else if(type.equals("data")) {// 大資料 sendMessage(mes, to); } } } /** * 發給指定-登入 * @param message * @param To * @param mark * @throws IOException */ public static void sendMessageLoginTo(String message, String To) throws IOException { //session.getBasicRemote().sendText(message); // 同步 //session.getAsyncRemote().sendText(message); // 非同步 if (loginMap != null) { for (WebSocket item : loginMap.values()) { if (item.uuid.equals(To) ) item.session.getAsyncRemote().sendText(message); } } } /** * 發給指定-大資料 * @param message * @param To * @param mark * @throws IOException */ public static void sendMessage(String message, String to) throws IOException { if (clients != null) { // 大資料 for(int i=0; i< clients.size(); i++) {// 這裡(迴圈中套了個迴圈判斷,用了執行緒加自帶的非同步推送) new WebSocketThread(i,clients, to, message).start();// 這裡寫到另一個類上了參考下面兩個(暫時不用)的就好 } } // 說明:如果多個訊息推送使用同一個session推送訊息,socket多執行緒衝突 // 報錯:webSocket多執行緒推送出錯,報錯資訊為The remote endpoint was in state [TEXT_PARTIAL_WRITING] // 建議使用:Thread.sleep(100); 根據同一個session判斷(自己寫咯) } /** * 發給指定-大資料(暫時不用) * @param message * @param To * @param mark * @throws IOException */ public static void sendMessageTo(String message, String to) throws IOException { if (clients != null) { // 大資料 for(int i=0; i< clients.size(); i++) { Map<String, WebSocket> clien = clients.get(i); for (WebSocket item : clien.values()) { if (item.uuid.equals(to) ) item.session.getAsyncRemote().sendText(message); } } } } /** * 發給全部 -大資料(暫時不用) * @param message * @throws IOException */ public static void sendMessageAll(String message) throws IOException { for(int i=0; i< clients.size(); i++) { Map<String, WebSocket> clien = clients.get(i); for (WebSocket item : clien.values()) { item.session.getAsyncRemote().sendText(message); } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocket.onlineCount++; } public static synchronized void subOnlineCount() { WebSocket.onlineCount--; } public static synchronized Map<String, WebSocket> getLoginMap() { return loginMap; } public static synchronized List<Map<String, WebSocket>> getClients() { return clients; } }

JS中
websocket是html5自帶的協議
這裡的二維碼生成方式和路徑引數我就不寫了
我是使用了UUID傳到手機確認的,UUID是我存放到後臺Java中的鍵值對標識

var websocket = null;
var host = document.location.host;
var judgeTemp = 0;
var temp_uuid;

// 其他地方呼叫初始化websocket連線
function openWebSorket(uuid){// uuid 是生成的二維碼引數傳到手機,然後確認登入後傳回來後臺對應推送的
	temp_uuid = uuid;
	createWebSorket(uuid);
}
function createWebSorket(uuid){
	try{
		//判斷當前瀏覽器是否支援WebSocket 
		if('WebSocket' in window) {
			websocket = new WebSocket('ws://' + host + '/securityIntelligence/webSocket/login/'+uuid);
			initEventHandle();
		} else {
			alert('當前瀏覽器無法使用掃碼登入');
		}
	}catch(err){
		loadTemp++;
		if(loadTemp == 5){// 重連5 次不行就重新重新整理瀏覽器
			window.location.reload();
		}
		console.log(err)
		console.log(err.message);
		setTimeout(function(){
			createWebSorket(uuid);// 由於網路問題可能無法連線(兩秒後重新連線)
		}, 2000)
	}
}
function initEventHandle(){
	//連線發生錯誤的回撥方法 
	websocket.onerror = function() {
		judgeTemp = 1;
		console.log('掃碼登入連線失敗');
	};
	//連線成功建立的回撥方法 
	websocket.onopen = function() {
		console.log('掃碼登入連線成功');
	}
	//接收到訊息的回撥方法 
	websocket.onmessage = function(event) {
		console.log('掃碼回饋訊息');
		setMessageInnerHTML(event.data);
	}
	//連線關閉的回撥方法 
	websocket.onclose = function() {
		console.log('掃碼登入連線關閉');
		if(judgeTemp != 1){
			createWebSorket(temp_uuid);
		}
	}
	//監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。 
	window.onbeforeunload = function() {
		judgeTemp = 1;
		closeWebSocket();
	}
}
//關閉WebSocket連線 
function closeWebSocket() {
	websocket.close();
}
/**
 * 手機端確定登入後頁面登入跳轉主頁
 * @returns
 */
function setMessageInnerHTML(data){
	judgeTemp = 1;
	if(tempNum == 0){// tempNum 全域性的防止重複點選登入用的,這裡直接拿過來的不改了
		tempNum = 1;
		$('#submit').addClass('layui-btn-disabled');
		$('#submit').html("正在登入中...");
		$.post(systemPath+"login/login", {},function(data) {// 這裡引數和推送的自己定義
			layer.msg(data.msg,{"zIndex":9999999999,"offset":"15%"})
			if (data.success) {// 登入成功了
				closeWebSocket();
				window.location.href = systemPath+"page/main";
			}
			tempNum = 0;
			$('#submit').removeClass('layui-btn-disabled');
			$('#submit').html("登&nbsp;&nbsp;&nbsp;&nbsp;錄");
		})
	}
}

以上就是我寫的websocket掃碼登入了,長連線也實現了
我沒有把所有的頁面程式碼放上來,但是文字已經說明了使用,看不懂我就沒有辦法咯
本人使用的框架是:
Spring + Spring MVC + Mybatis + Sql server + Layui

釋出後Nginx代理報錯問題解決:
websocket連線錯誤Error during WebSocket handshake Unexpected response code 404

 location /ProjectName {
       proxy_pass http://temp_project;
       proxy_redirect default;
       proxy_connect_timeout  5s;   #預設值60s, nginx連線到後端伺服器的連線超時時間
       proxy_set_header Host $host:80;
       proxy_set_header X-Forward-For $remote_addr;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_read_timeout 3600s;
}

主要關鍵行,使用如下的即可

	   proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_read_timeout 3600s;