亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java應用層協議WebSocket如何實現消息推送

發布時間:2023-02-22 11:36:54 來源:億速云 閱讀:133 作者:iii 欄目:開發技術

這篇“Java應用層協議WebSocket如何實現消息推送”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Java應用層協議WebSocket如何實現消息推送”文章吧。

前言

大部分的web開發者,開發的業務都是基于Http協議的:前端請求后端接口,攜帶參數,后端執行業務代碼,再返回結果給前端。作者參與開發的項目,有一個報警推送的功能,服務端實時推送報警信息給瀏覽器端;還有像抖音里面,如果有人關注、回復你的評論時,抖音就會推送相關消息給你了,你就會收到一條消息。

有些同學會說了,基于Http協議也能實現啊:前端定時訪問后端(每隔3s或者幾秒),后端返回消息數據,前端拿到后彈出消息。這種方式太low了,而且每個瀏覽器都這樣,使用系統的人一多,服務器的壓力就太大了些。那到底用什么技術手段實現呢?我們的主角就登場了。

WebSocket是在單個TCP連接上進行全雙工通信的應用層協議(Http協議也是應用層),瀏覽器端和服務端都可主動發送數據給另一端。這樣是不是比Http協議更適合消息推送這種場景。

瀏覽器端

建一個SpringBoot項目,Html放在src\main\resources\static下:

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
<!--    解決中文亂碼-->
    <meta charset="UTF-8"/>
    <title></title>
    <script type="text/javascript" src="./js/jquery.min.js"></script>
</head>
<body>
    <input id="input1" type="text" /><br/>
    <input type="button" value="瀏覽器發送服務端" onclick="btnClick()" />
    <input type="button" value="服務端發送瀏覽器" onclick="btnClick1()" />
    <input type="button" value="重新打開連接" onclick="btnClick2()" />
    <br/>
    <textarea id="textArea" ></textarea>
<script>
    var ws;
    webSocketInit();
    function webSocketInit() {
        ws =new WebSocket('ws://localhost:8080/bootdemo/webSocket/10086');
        // 獲取連接狀態
        console.log('ws連接狀態[初始]:' + ws.readyState);
        //監聽是否連接成功
        ws.onopen = function () {
            console.log('ws連接狀態[成功]:' + ws.readyState);
        };
        // 接聽服務器發回的信息并處理展示
        ws.onmessage = function (obj) {
            console.log('接收到來自服務器的消息:');
            var txt = $("#textArea").val();
            $("#textArea").val(txt + "\n" + obj.data);
            $("#textArea").scrollTop($("#textArea")[0].scrollHeight);
            //完成通信后關閉WebSocket連接
            // ws.close();
        };
        // 監聽連接關閉事件
        ws.onclose = function () {
            // 監聽整個過程中websocket的狀態
            console.log('ws連接狀態[關閉]:' + ws.readyState);
        };
        // 監聽并處理error事件
        ws.onerror = function (error) {
            console.log(error);
        };
    }
    function btnClick() {
        console.log("瀏覽器端發送消息:");
        //連接成功則發送一個數據
        ws.send($("#input1").val());
    }
    function btnClick1() {
        $.ajax({
            url: 'http://localhost:8080/bootdemo/pushWebSocket/publish?' +
            'userId=10086&message=' + $("#input1").val(),
            type: 'GET',
            success: function (data) {
                // console.log(data);
            }
        });
    }
    function btnClick2() {
        webSocketInit();
    }
</script>
</body>
</html>

服務器端

先引入依賴:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

bean上添加@ServerEndpoint,作為WebSocket的服務端。

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@Slf4j
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketServer {
    //與某個客戶端的連接會話,需要通過它來給客戶端發送數據
    private Session session;
    private static final CopyOnWriteArraySet<WebSocketServer> webSockets =
    new CopyOnWriteArraySet<>();
    // 用來存在線連接數
    private static final Map<String, Session> sessionPool = 
    new HashMap<String, Session>();
    /**
     * 連接成功調用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "userId") 
    String userId) {
        try {
            this.session = session;
            webSockets.add(this);
            sessionPool.put(userId, session);
        }
        catch (Exception e) {
        }
    }
    /**
     * 收到客戶端消息后調用的方法
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("websocket消息: 收到客戶端消息:" + message);
    }
    public void sendOneMessage(String userId, String message) {
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            try {
                log.info("服務端推送消息:" + message);
                session.getAsyncRemote().sendText(message);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

進行注冊:

@Configuration
public class WebSocketConfigOne {
    /**
     * 這個bean會自動注冊使用了@ServerEndpoint注解聲明的對象
     * 沒有的話會報404
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

推送消息的控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/pushWebSocket")
public class WebSocketController {
    @Autowired
    private WebSocketServer webSocketServer;
    @GetMapping("/publish")
    @ResponseBody
    public Map publish(String userId, String message) {
        webSocketServer.sendOneMessage(userId, message);
        HashMap<String, Object> map = new HashMap<>();
        map.put("code", 200);
        return map;
    }
}

還有我的配置文件application.properties:

# web port

server.port=8080

server.servlet.context-path=/bootdemo

運行啟動類后,訪問html(localhost:8080/bootdemo/index.html)如下:

Java應用層協議WebSocket如何實現消息推送

有的同學一思索,點擊圖中的第2個按鈕"服務端發送瀏覽器",你這好像也是前端先請求,再推送的消息;我們的WebSocketController#publish方法,在真實的場景下,可以在后端的定時任務中、消息中間件的消費者端調用,不用前端先發送請求。

當然SpringBoot有專門構建WebSocket服務端的方式。

核心配置類:

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@Configuration
@EnableWebSocket
@Slf4j
public class WebSocketConfig1 implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry 
    registry) {
        registry.addHandler(new MyWebSocketHandler(), "/webSocket/{userId}")//設置連接路徑和處理
                .setAllowedOrigins("*")
                .addInterceptors(new MyWebSocketInterceptor());//設置攔截器
    }
    class MyWebSocketInterceptor implements HandshakeInterceptor {
        //前置攔截一般用來注冊用戶信息,綁定 WebSocketSession
        @Override
        public boolean beforeHandshake(ServerHttpRequest request, 
        ServerHttpResponse response, WebSocketHandler wsHandler, 
        Map<String, Object> attributes) throws Exception {
            log.info("前置攔截~~");
            if (!(request instanceof ServletServerHttpRequest)) {
                return true;
            }
            HttpServletRequest servletRequest = 
            ((ServletServerHttpRequest)request).getServletRequest();
            Map map = (Map)servletRequest.getAttribute(HandlerMapping.
            URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            String userId = (String)map.get("userId");
            attributes.put("userId", userId);
            return true;
        }
        @Override
        public void afterHandshake(ServerHttpRequest request, 
        ServerHttpResponse response, WebSocketHandler wsHandler, 
        Exception exception) {
            log.info("后置攔截~~");
        }
    }
}

核心處理器:

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class MyWebSocketHandler implements WebSocketHandler {
    private static final Map<String, WebSocketSession> SESSIONS = 
    new ConcurrentHashMap<>();
	/**
	 * 建立新的socket連接后回調的方法
	 */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) 
    throws Exception {
        String userId = (String) session.getAttributes().get("userId");
        SESSIONS.put(userId, session);
    }
	/**
	 * 接收到瀏覽器端的消息后回調的方法
	 */
    @Override
    public void handleMessage(WebSocketSession session, 
    WebSocketMessage<?> message) throws Exception {
        String msg = message.getPayload().toString();
        log.info("收到客戶端消息:" + msg);
    }
	/**
	 * 連接出錯時回調的方法
	 */
    @Override
    public void handleTransportError(WebSocketSession session, 
    Throwable exception) throws Exception {
        log.info("連接出錯");
        if (session.isOpen()) {
            session.close();
        }
    }
	/**
	 * 連接關閉時回調的方法
	 */
    @Override
    public void afterConnectionClosed(WebSocketSession session, 
    CloseStatus closeStatus) throws Exception {
        log.info("連接關閉:status:" + closeStatus);
    }
	/**
	 * 是否處理部分消息,返回false就行
	 */
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
	/**
	 * 推送消息給瀏覽器端
	 */
    public void sendMessage(String userId, String message) {
        WebSocketSession webSocketSession = SESSIONS.get(userId);
        if (webSocketSession == null || !webSocketSession.isOpen()) {
            return;
        }
        try {
            webSocketSession.sendMessage(new TextMessage(message));
        }
        catch (Exception ex) {
            log.error("推送消息異常:" + ex);
        }
    }
}

控制器也改造下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/pushWebSocket")
public class WebSocketController {
    @Autowired
    private MyWebSocketHandler handler;
    @GetMapping("/publish")
    @ResponseBody
    public Map publish(String userId, String message) {
        handler.sendMessage(userId, message);
        HashMap<String, Object> map = new HashMap<>();
        map.put("code", 200);
        return map;
    }
}

前端部分不用做修改,和之前一樣的代碼。

以上就是關于“Java應用層協議WebSocket如何實現消息推送”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

海宁市| 大宁县| 庐江县| 平罗县| 巴林右旗| 榆树市| 昔阳县| 顺义区| 福海县| 理塘县| 苍山县| 定西市| 新田县| 盐山县| 镇雄县| 汤原县| 梁河县| 黎城县| 乐平市| 武威市| 体育| 芮城县| 霞浦县| 南川市| 辛集市| 新巴尔虎左旗| 信阳市| 巴塘县| 兴仁县| 岑巩县| 苍山县| 昌图县| 平谷区| 巴南区| 竹溪县| 清流县| 平泉县| 塔城市| 资源县| 遵化市| 焦作市|