SSM框架整合WebSocket實現訊息推送長連線,WebSocket實現掃碼登入
阿新 • • 發佈:2020-12-16
技術標籤:前端後端前後端同效果測試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("登 錄");
})
}
}
以上就是我寫的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;