SpringBoot使用WebSocket推送消息到瀏覽器

WebSocket

簡介

WebSocket協議支持(在受控環境中運行不受信任的代碼的)客戶端與(選擇加入該代碼的通訊的)遠程主機之間進行全雙工通訊。用於此的安全模型是Web瀏覽器經常使用的基於原始的安全模式。 協議包括一個開放的握手以及隨後的TCP層上的消息幀。 該技術的目標是爲基於瀏覽器的、須要和服務器進行雙向通訊的(服務器不能依賴於打開多個HTTP鏈接(例如,使用XMLHttpRequest或<iframe>和長輪詢))應用程序提供一種通訊機制。javascript

實現原理

在實現websocket連線過程當中,須要經過瀏覽器發出websocket連線請求,而後服務器發出迴應,這個過程一般稱爲「握手」 。在 WebSocket API,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。在此WebSocket 協議中,爲咱們實現即時服務帶來了兩大好處:java

1. Headerweb

互相溝通的Header是很小的-大概只有 2 Bytesspring

2. Server Push瀏覽器

服務器的推送,服務器再也不被動的接收到瀏覽器的請求以後才返回數據,而是在有新數據時就主動推送給瀏覽器。安全

HTML5 API

在HTML5中內置有一些API,用於響應應用程序發起的請求。基本API語句以下:
建立對象
1
var ws = new WebSocket(url,name);
url爲WebSocket服務器的地址,name爲發起握手的協議名稱,爲可選擇項。
發送文本消息
ws.send(msg);
msg爲文本消息,對於其餘類型的能夠經過二進制形式發送。

接收消息
ws.onmessage = (function(){...})();

錯誤處理
ws.onerror = (function(){...})();

關閉鏈接
ws.close();

SpringBoot-WebSocket使用

引入

在pom文件中引入WebSocket相關jar包服務器

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

配置

/**
 *  WebSocket 配置
 */
@Configuration
public class WebSocketConfig {

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

服務端鏈接

import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket 服務器
 */
@Component
@Slf4j
@ServerEndpoint("/ws/{userId}")
public class WebSocketServer {
    private static final AtomicInteger OnlineCount = new AtomicInteger(0);
    // concurrent包的線程安全Set,用來存放每一個客戶端對應的Session對象。
    private static CopyOnWriteArraySet<WebSocketServer> socketServers = new CopyOnWriteArraySet<WebSocketServer>();

    //用戶id 
    private Long userId;

    //當前會話
    private Session session;



    /**
     * 鏈接創建成功調用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("userId")Long userId, Session session) {
        socketServers.add(this);
        this.session=session;
        System.out.println(session.toString());
        this.userId=userId;
        int cnt = OnlineCount.incrementAndGet(); // 在線數加1
        log.info("有鏈接加入,當前鏈接數爲:{}", cnt);
        try {
            sendMessage("鏈接服務器成功");
        } catch (IOException e) {
            log.error("鏈接IO異常:{}",e.getMessage());
        }
    }

    /**
     * 鏈接關閉調用的方法
     */
    @OnClose
    public void onClose(Session session) {
        socketServers.remove(this);
        int cnt = OnlineCount.decrementAndGet();
        log.info("有鏈接關閉,當前鏈接數爲:{}", cnt);
    }


    /**
     * 收到客戶端消息後調用的方法
     *
     * @param message 客戶端發送過來的消息*/
    @OnMessage
    public void onMessage(String message, Session session) {
        //羣發消息
        for (WebSocketServer item : socketServers) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 發送消息,實踐代表,每次瀏覽器刷新,session會發生變化。
     * @param message
     */

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


    /**
     * 出現錯誤
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發生錯誤:{},Session ID: {}",error.getMessage(),session.getId());
        error.printStackTrace();
    }

    /**
     *  給某個具體的用戶推送消息
     * @param message
     * @param userId
     * @throws IOException
     */
    public  void sendMessageTo(@Nullable String message,@Nullable Long userId){


        for(WebSocketServer item :socketServers){
            if(item.userId.equals(userId)){
                try {
                    item.session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    log.error("推送消息:{} 給用戶{}出現IO異常",message,userId);
                }
            }
        }
    }

}

 

客戶端鏈接

//登陸成功後 進入主頁面,鏈接服務器websocket 接收服務器推送過來的消息
			connectWebSocket(userId);	
			function connectWebSocket(){
				
				var websocket = null;
				 //判斷當前瀏覽器是否支持webSocket
				 if('WebSocket' in window){
				
				   websocket = new WebSocket("ws://127.0.0.1:8080/ws/"+userId);
				 }else{
					 alert("您的瀏覽器不支持網頁消息推送");
				 }
				 
				 // 連接發生錯誤的回調方法
				  websocket.onerror = function(){
					  console.log("error");
				  }
				 // 連接成功創建的回調方法
				 websocket.onopen = function(event){
				   console.log("open");
				 }
				 // 接受到消息的回調方法
				 websocket.onmessage = function(event){
				 //在這裏將服務器推送過來的消息 根據本身的業務進行處理
				  console.log(event.data);
						
				 }
				 // 連接關閉的回調方法
				websocket.onclose = function(){
					console.log("close");
				}

				//監聽窗口關閉事件,當窗口關閉時,主動關閉wesocket 連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
				window.onbeforeunload = function(){
					websocket.close();
				}
				
				
				// 關閉連接
				function closeWebSocket(){
					websocket.close();
				}
			 
		}
相關文章
相關標籤/搜索