最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java應(yīng)用層協(xié)議WebSocket實(shí)現(xiàn)消息推送

 更新時(shí)間:2023年02月22日 08:57:00   作者:尚少  
后端向前端推送消息就需要長(zhǎng)連接,首先想到的就是websocket,下面這篇文章主要給大家介紹了關(guān)于java后端+前端使用WebSocket實(shí)現(xiàn)消息推送的詳細(xì)流程,需要的朋友可以參考下

前言

  大部分的web開發(fā)者,開發(fā)的業(yè)務(wù)都是基于Http協(xié)議的:前端請(qǐng)求后端接口,攜帶參數(shù),后端執(zhí)行業(yè)務(wù)代碼,再返回結(jié)果給前端。作者參與開發(fā)的項(xiàng)目,有一個(gè)報(bào)警推送的功能,服務(wù)端實(shí)時(shí)推送報(bào)警信息給瀏覽器端;還有像抖音里面,如果有人關(guān)注、回復(fù)你的評(píng)論時(shí),抖音就會(huì)推送相關(guān)消息給你了,你就會(huì)收到一條消息。

  有些同學(xué)會(huì)說了,基于Http協(xié)議也能實(shí)現(xiàn)?。呵岸硕〞r(shí)訪問后端(每隔3s或者幾秒),后端返回消息數(shù)據(jù),前端拿到后彈出消息。這種方式太low了,而且每個(gè)瀏覽器都這樣,使用系統(tǒng)的人一多,服務(wù)器的壓力就太大了些。那到底用什么技術(shù)手段實(shí)現(xiàn)呢?我們的主角就登場(chǎng)了。

  WebSocket是在單個(gè)TCP連接上進(jìn)行全雙工通信的應(yīng)用層協(xié)議(Http協(xié)議也是應(yīng)用層),瀏覽器端和服務(wù)端都可主動(dòng)發(fā)送數(shù)據(jù)給另一端。這樣是不是比Http協(xié)議更適合消息推送這種場(chǎng)景。

瀏覽器端

  作者建了一個(gè)SpringBoot項(xiàng)目,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="瀏覽器發(fā)送服務(wù)端" onclick="btnClick()" />
    <input type="button" value="服務(wù)端發(fā)送瀏覽器" onclick="btnClick1()" />
    <input type="button" value="重新打開連接" onclick="btnClick2()" />
    <br/>
    <textarea id="textArea" style="height: 50px"></textarea>
<script>
    var ws;
    webSocketInit();
    function webSocketInit() {
        ws =new WebSocket('ws://localhost:8080/bootdemo/webSocket/10086');
        // 獲取連接狀態(tài)
        console.log('ws連接狀態(tài)[初始]:' + ws.readyState);
        //監(jiān)聽是否連接成功
        ws.onopen = function () {
            console.log('ws連接狀態(tài)[成功]:' + ws.readyState);
        };
        // 接聽服務(wù)器發(fā)回的信息并處理展示
        ws.onmessage = function (obj) {
            console.log('接收到來自服務(wù)器的消息:');
            var txt = $("#textArea").val();
            $("#textArea").val(txt + "\n" + obj.data);
            $("#textArea").scrollTop($("#textArea")[0].scrollHeight);
            //完成通信后關(guān)閉WebSocket連接
            // ws.close();
        };
        // 監(jiān)聽連接關(guān)閉事件
        ws.onclose = function () {
            // 監(jiān)聽整個(gè)過程中websocket的狀態(tài)
            console.log('ws連接狀態(tài)[關(guān)閉]:' + ws.readyState);
        };
        // 監(jiān)聽并處理error事件
        ws.onerror = function (error) {
            console.log(error);
        };
    }
    function btnClick() {
        console.log("瀏覽器端發(fā)送消息:");
        //連接成功則發(fā)送一個(gè)數(shù)據(jù)
        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>

服務(wù)器端

  先引入依賴:

    <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的服務(wù)端。

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 {
    //與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
    private Session session;
    private static final CopyOnWriteArraySet<WebSocketServer> webSockets =
    new CopyOnWriteArraySet<>();
    // 用來存在線連接數(shù)
    private static final Map<String, Session> sessionPool = 
    new HashMap<String, Session>();
    /**
     * 連接成功調(diào)用的方法
     */
    @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) {
        }
    }
    /**
     * 收到客戶端消息后調(diào)用的方法
     */
    @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("服務(wù)端推送消息:" + message);
                session.getAsyncRemote().sendText(message);
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

  進(jìn)行注冊(cè):

@Configuration
public class WebSocketConfigOne {
    /**
     * 這個(gè)bean會(huì)自動(dòng)注冊(cè)使用了@ServerEndpoint注解聲明的對(duì)象
     * 沒有的話會(huì)報(bào)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

  運(yùn)行啟動(dòng)類后,訪問html(localhost:8080/bootdemo/index.html)如下:

  有的同學(xué)一思索,點(diǎn)擊圖中的第2個(gè)按鈕"服務(wù)端發(fā)送瀏覽器",你這好像也是前端先請(qǐng)求,再推送的消息;我們的WebSocketController#publish方法,在真實(shí)的場(chǎng)景下,可以在后端的定時(shí)任務(wù)中、消息中間件的消費(fèi)者端調(diào)用,不用前端先發(fā)送請(qǐng)求。

  當(dāng)然SpringBoot有專門構(gòu)建WebSocket服務(wù)端的方式。

  核心配置類:

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}")//設(shè)置連接路徑和處理
                .setAllowedOrigins("*")
                .addInterceptors(new MyWebSocketInterceptor());//設(shè)置攔截器
    }
    class MyWebSocketInterceptor implements HandshakeInterceptor {
        //前置攔截一般用來注冊(cè)用戶信息,綁定 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連接后回調(diào)的方法
	 */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) 
    throws Exception {
        String userId = (String) session.getAttributes().get("userId");
        SESSIONS.put(userId, session);
    }
	/**
	 * 接收到瀏覽器端的消息后回調(diào)的方法
	 */
    @Override
    public void handleMessage(WebSocketSession session, 
    WebSocketMessage<?> message) throws Exception {
        String msg = message.getPayload().toString();
        log.info("收到客戶端消息:" + msg);
    }
	/**
	 * 連接出錯(cuò)時(shí)回調(diào)的方法
	 */
    @Override
    public void handleTransportError(WebSocketSession session, 
    Throwable exception) throws Exception {
        log.info("連接出錯(cuò)");
        if (session.isOpen()) {
            session.close();
        }
    }
	/**
	 * 連接關(guān)閉時(shí)回調(diào)的方法
	 */
    @Override
    public void afterConnectionClosed(WebSocketSession session, 
    CloseStatus closeStatus) throws Exception {
        log.info("連接關(guān)閉: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;
    }
}

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

到此這篇關(guān)于Java應(yīng)用層協(xié)議WebSocket實(shí)現(xiàn)消息推送的文章就介紹到這了,更多相關(guān)Java WebSocket內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringMVC中的ConversionServiceExposingInterceptor工具類解析

    SpringMVC中的ConversionServiceExposingInterceptor工具類解析

    這篇文章主要介紹了SpringMVC中的ConversionServiceExposingInterceptor工具類解析,ConversionServiceExposingInterceptor是Spring MVC的一個(gè)HandlerInterceptor,用于向請(qǐng)求添加一個(gè)屬性,需要的朋友可以參考下
    2023-12-12
  • 關(guān)于springboot 配置文件中屬性變量引用方式@@解析

    關(guān)于springboot 配置文件中屬性變量引用方式@@解析

    這篇文章主要介紹了關(guān)于springboot 配置文件中屬性變量引用方式@@解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • Java?try?catch語句異常處理詳解

    Java?try?catch語句異常處理詳解

    這篇文章主要給大家介紹了關(guān)于Java?try?catch語句異常處理的相關(guān)資料,Java中的try-catch用于捕獲和處理異常,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • Java 回調(diào)機(jī)制(CallBack) 詳解及實(shí)例代碼

    Java 回調(diào)機(jī)制(CallBack) 詳解及實(shí)例代碼

    這篇文章主要介紹了 Java 回調(diào)機(jī)制(CallBack) 詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java實(shí)現(xiàn)通訊錄管理系統(tǒng)項(xiàng)目

    Java實(shí)現(xiàn)通訊錄管理系統(tǒng)項(xiàng)目

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)通訊錄管理系統(tǒng)項(xiàng)目,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java Dubbo協(xié)議下的服務(wù)端線程使用詳解

    Java Dubbo協(xié)議下的服務(wù)端線程使用詳解

    Dubbo是阿里開源項(xiàng)目,國(guó)內(nèi)很多互聯(lián)網(wǎng)公司都在用,已經(jīng)經(jīng)過很多線上考驗(yàn)。Dubbo內(nèi)部使用了Netty、Zookeeper,保證了高性能高可用性,使用Dubbo可以將核心業(yè)務(wù)抽取出來,作為獨(dú)立的服務(wù),逐漸形成穩(wěn)定的服務(wù)中心
    2023-03-03
  • MyBatis環(huán)境資源配置實(shí)現(xiàn)代碼詳解

    MyBatis環(huán)境資源配置實(shí)現(xiàn)代碼詳解

    這篇文章主要介紹了MyBatis環(huán)境資源配置實(shí)現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java后端接口中提取請(qǐng)求頭中的Cookie和Token的方法

    Java后端接口中提取請(qǐng)求頭中的Cookie和Token的方法

    在現(xiàn)代 Web 開發(fā)中,HTTP 請(qǐng)求頭(Header)是客戶端與服務(wù)器之間傳遞信息的重要方式之一,本文將詳細(xì)介紹如何在 Java 后端(以 Spring Boot 為例)中提取請(qǐng)求頭中的 Cookie 和 Token,并提供完整的代碼示例和優(yōu)化建議,需要的朋友可以參考下
    2025-01-01
  • Mybatis 返回值類型和參數(shù)傳遞的配置方法

    Mybatis 返回值類型和參數(shù)傳遞的配置方法

    在 MyBatis 中,返回值類型和參數(shù)傳遞是 Mapper 接口中至關(guān)重要的兩個(gè)方面,正確理解和使用它們可以幫助我們高效、準(zhǔn)確地進(jìn)行數(shù)據(jù)庫(kù)操作,接下來通過本文給大家介紹Mybatis 返回值類型和參數(shù)傳遞的配置方法,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • Java連接sftp服務(wù)器實(shí)現(xiàn)上傳下載功能

    Java連接sftp服務(wù)器實(shí)現(xiàn)上傳下載功能

    這篇文章主要介紹了java連接sftp服務(wù)器實(shí)現(xiàn)上傳下載,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08

最新評(píng)論

宜良县| 山西省| 阿城市| 苍梧县| 沙河市| 项城市| 项城市| 烟台市| 孟津县| 保靖县| 隆昌县| 珠海市| 盱眙县| 浦东新区| 留坝县| 吴忠市| 桃江县| 合阳县| 凭祥市| 雅江县| 神农架林区| 石嘴山市| 平利县| 灌云县| 澄迈县| 嘉兴市| 景东| 平乐县| 成安县| 澜沧| 孙吴县| 伊金霍洛旗| 德钦县| 开封县| 陆河县| 资源县| 宁波市| 封开县| 随州市| 宁晋县| 吴忠市|