国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè))

這篇具有很好參考價(jià)值的文章主要介紹了前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè))。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

前言

寫(xiě)這個(gè)項(xiàng)目主要是有有個(gè)項(xiàng)目需要后端有數(shù)據(jù)實(shí)話返回前端,一開(kāi)始采用前端輪詢(xún)的方式,后面覺(jué)得及時(shí)性上有些不行,然后改為使用websocket ,具體實(shí)現(xiàn)demo以及測(cè)試流程發(fā)出來(lái)提供交流學(xué)習(xí),

一、Web Socket 簡(jiǎn)紹

WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。WebSocket通信協(xié)議于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并由RFC7936補(bǔ)充規(guī)范。WebSocket API也被W3C定為標(biāo)準(zhǔn)。
WebSocket使得客戶(hù)端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶(hù)端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。

1 為什么用 websocket?

換句話說(shuō),websocket 解決了什么問(wèn)題?答案是,解決了兩個(gè)主要問(wèn)題:

  • 只能客戶(hù)端發(fā)送請(qǐng)求
  • 一段時(shí)間內(nèi)的頻繁信息發(fā)送

假設(shè)現(xiàn)在需要設(shè)計(jì)一個(gè)實(shí)時(shí)預(yù)警系統(tǒng)的通知模塊,那么作為工程師我們應(yīng)該怎么設(shè)計(jì)通知的這個(gè)功能呢?因?yàn)檫@些系統(tǒng)的數(shù)據(jù)來(lái)源,一般他通過(guò)硬件設(shè)備采集到后臺(tái)的,如果我們現(xiàn)在只有 http 協(xié)議,那么我們只能讓客戶(hù)端不斷地輪詢(xún)服務(wù)器,輪詢(xún)的時(shí)間間隔越小越能接近實(shí)時(shí)的效果??墒?,輪詢(xún)的效率低,又浪費(fèi)資源。針對(duì)這樣的場(chǎng)景,websocket 應(yīng)運(yùn)而生。

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議
特點(diǎn):
(1)建立在 TCP 協(xié)議之上,服務(wù)器端的實(shí)現(xiàn)比較容易,是一個(gè)可靠的傳輸協(xié)議。
(2)與 HTTP協(xié)議有著良好的兼容性。默認(rèn)端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時(shí)不容易屏蔽,能通過(guò)各種 HTTP 代理服務(wù)器。
(3)數(shù)據(jù)格式比較輕量,性能開(kāi)銷(xiāo)小,通信高效。
(4)可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。
(5)沒(méi)有同源限制,客戶(hù)端可以與任意服務(wù)器通信。
(6)協(xié)議標(biāo)識(shí)符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是 URL。

二、代碼實(shí)現(xiàn)

1、前端(html)

1.1、無(wú)前端向后端發(fā)送消息

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

uid實(shí)際開(kāi)發(fā)中應(yīng)該使用唯一值作為當(dāng)前對(duì)話的key

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
消息展示區(qū):<br/>
<div id="textArea"></div>
 
</body>
<script>
    var textArea = document.getElementById('textArea');
 
 
    var websocket = null;
    //如果瀏覽器支持websocket就建立一個(gè)websocket,否則提示瀏覽器不支持websocket 
                //uid應(yīng)該要用唯一標(biāo)識(shí),為了測(cè)試方便看
    if('websocket' in window){
        websocketPage = new WebSocket('ws://localhost:8080/websocket/' + 99);
    }else{
        alert('瀏覽器不支持websocket!');
    }
    //建立websocket時(shí)自動(dòng)調(diào)用
    websocketPage.onopen = function (event) {
        console.log('建立連接');
    }
    //關(guān)閉webscoket時(shí)自動(dòng)調(diào)用
    websocketPage.oncolse = function (event){
        console.log('關(guān)閉連接');
    }
    //websocket接收到消息時(shí)調(diào)用
    websocketPage.onmessage = function (event){
        //將接收到的消息展示在消息展示區(qū)  (心跳響應(yīng)回來(lái)的消息不顯示)
        if (event.data !== "conn_success"){
            textArea.innerText += event.data;
            textArea.innerHTML += "<br/>";
        }
    }
    //websocket出錯(cuò)自動(dòng)調(diào)用
    websocketPage.onerror = function () {
        alert('websocket出錯(cuò)');
    }
    //關(guān)閉窗口前關(guān)閉websocket連接
    window.onbeforeunload = function (){
        websocketPage.close();
    }
 
</script>
</html>

1.2、有前端向后端發(fā)送消息

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

<!DOCTYPE html>
<html>

	<head>
		<meta charset="utf-8">
		<title>Java后端WebSocket的Tomcat實(shí)現(xiàn)</title>
		<script type="text/javascript" src="js/jquery.min.js"></script>
	</head>

	<body>
		
		Welcome<br/><input id="text" type="text" />
		<button onclick="send()">發(fā)送消息</button>
		<hr/>
		<button onclick="closeWebSocket()">關(guān)閉WebSocket連接</button>
		<hr/>
		<div id="message"></div>
	</body>
	<script type="text/javascript">
		var websocket = null;
		//判斷當(dāng)前瀏覽器是否支持WebSocket
		if('WebSocket' in window) {
			//改成你的地址
			websocket = new WebSocket("ws://localhost:8080/websocket/100");
		} else {
			alert('當(dāng)前瀏覽器 Not support websocket')
		}

		//連接發(fā)生錯(cuò)誤的回調(diào)方法
		websocket.onerror = function() {
			setMessageInnerHTML("WebSocket連接發(fā)生錯(cuò)誤");
		};

		//連接成功建立的回調(diào)方法
		websocket.onopen = function() {
			setMessageInnerHTML("WebSocket連接成功");
		}
		var U01data, Uidata, Usdata
		//接收到消息的回調(diào)方法
		websocket.onmessage = function(event) {
			console.log(event);
			if (event.data !== "conn_success"){
				setMessageInnerHTML("接收消息:"+event.data);
				// setMessageInnerHTML(event);
				setechart()
			}
		}

		//連接關(guān)閉的回調(diào)方法
		websocket.onclose = function() {
			setMessageInnerHTML("WebSocket連接關(guān)閉");
		}

		// //監(jiān)聽(tīng)窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí),主動(dòng)去關(guān)閉websocket連接,防止連接還沒(méi)斷開(kāi)就關(guān)閉窗口,server端會(huì)拋異常。
		window.onbeforeunload = function() {
			closeWebSocket();
		}

		//將消息顯示在網(wǎng)頁(yè)上
		function setMessageInnerHTML(innerHTML) {
			document.getElementById('message').innerHTML += innerHTML + '<br/>';
		}

		//關(guān)閉WebSocket連接
		function closeWebSocket() {
			websocket.close();
		}

		//發(fā)送消息
		function send() {
			var message = document.getElementById('text').value;
			websocket.send('{"msg":"' + message + '"}');
			setMessageInnerHTML("--------------發(fā)送消息:"+message + "");
		}
	</script>
</html>

2、后端具體代碼(spring boot)

2.1、maven依賴(lài)

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

– yml沒(méi)有東西,只有一個(gè)默認(rèn)端口

2.2、配置類(lèi)

需要加一個(gè) WebSocket 端點(diǎn)暴露 的bean 和定時(shí)器注解

@EnableScheduling  //定時(shí)器
@SpringBootApplication
public class WebSocketApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebSocketApplication.class, args);
    }



    /** 
     * 服務(wù)器端點(diǎn)導(dǎo)出 
     * @author zhengfuping
     * @date 2023/8/22 
     * @return ServerEndpointExporter 
     */
    @Bean
    public ServerEndpointExporter getServerEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

2.3、Web Socket連接工具類(lèi)

@Slf4j
@Service
@ServerEndpoint("/websocket/{uid}")
public class WebSocketServer2 {

    //連接建立時(shí)長(zhǎng)
    private static final long sessionTimeout = 60000;

    // 用來(lái)存放每個(gè)客戶(hù)端對(duì)應(yīng)的WebSocketServer對(duì)象
    private static Map<String, WebSocketServer2> webSocketMap = new ConcurrentHashMap<>();

    // 與某個(gè)客戶(hù)端的連接會(huì)話,需要通過(guò)它來(lái)給客戶(hù)端發(fā)送數(shù)據(jù)
    private Session session;

    // 接收id
    private String uid;

    /**
     * 連接建立成功調(diào)用的方法
     * @author zhengfuping
     * @date 2023/8/22
     * @param session
     * @param uid
     */
    @OnOpen
    public void onOpen(Session session , @PathParam("uid") String uid){
        session.setMaxIdleTimeout(sessionTimeout);
        this.session = session;
        this.uid = uid;
        if (webSocketMap.containsKey(uid)){
            webSocketMap.remove(uid);
        }
        webSocketMap.put(uid,this);
        log.info("websocket連接成功編號(hào)uid: " + uid + ",當(dāng)前在線數(shù): " + getOnlineClients());

        try{
        // 響應(yīng)客戶(hù)端實(shí)際業(yè)務(wù)數(shù)據(jù)!
            sendMessage("conn_success");
        }catch (Exception e){
            log.error("websocket發(fā)送連接成功錯(cuò)誤編號(hào)uid: " + uid + ",網(wǎng)絡(luò)異常!!!");
        }
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     * @author zhengfuping
     * @date 2023/8/22
     */
    @OnClose
    public void onClose(){
        try {
            if (webSocketMap.containsKey(uid)){
                webSocketMap.remove(uid);
            }
            log.info("websocket退出編號(hào)uid: " + uid + ",當(dāng)前在線數(shù)為: " + getOnlineClients());
        } catch (Exception e) {
            log.error("websocket編號(hào)uid連接關(guān)閉錯(cuò)誤: " + uid + ",原因: " + e.getMessage());
        }
    }


    /**
     * 收到客戶(hù)端消息后調(diào)用的方法
     * @param message 客戶(hù)端發(fā)送過(guò)來(lái)的消息
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            WebSocketServer2.sendInfo(message);
            log.info("websocket收到客戶(hù)端編號(hào)uid消息: " + uid + ", 報(bào)文: " + message);
        } catch (Exception e) {
            log.error("websocket發(fā)送消息失敗編號(hào)uid為: " + uid + ",報(bào)文: " + message);
        }

    }

    /**
     * 發(fā)生錯(cuò)誤時(shí)調(diào)用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("websocket編號(hào)uid錯(cuò)誤: " + this.uid + "原因: " + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送
     * @author yingfeng
     * @date 2023/8/22 10:11
     * @Param * @param null
     * @return
     */

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

    /**
     * 獲取客戶(hù)端在線數(shù)
     * @author zhengfuping
     * @date 2023/8/22 10:11
     * @param
     */
    public static synchronized int getOnlineClients() {
        if (Objects.isNull(webSocketMap)) {
            return 0;
        } else {
            return webSocketMap.size();
        }
    }




    /**
     * 單機(jī)使用,外部接口通過(guò)指定的客戶(hù)id向該客戶(hù)推送消息
     * @param key
     * @param message
     * @return boolean
     */
    public static boolean sendMessageByWayBillId(@NotNull String key, String message) {
        WebSocketServer2 webSocketServer = webSocketMap.get(key);
        if (Objects.nonNull(webSocketServer)) {
            try {
                webSocketServer.sendMessage(message);
                log.info("websocket發(fā)送消息編號(hào)uid為: " + key + "發(fā)送消息: " + message);
                return true;
            } catch (Exception e) {
                log.error("websocket發(fā)送消息失敗編號(hào)uid為: " + key + "消息: " + message);
                return false;
            }
        } else {
            log.error("websocket未連接編號(hào)uid號(hào)為: " + key + "消息: " + message);
            return false;
        }
    }

    /**
     * 群發(fā)自定義消息
     * @author zhengfuping
     * @date 2023/8/22 9:52
     * @param message
     */
    public static void sendInfo(String message) {
        webSocketMap.forEach((k, v) -> {
            WebSocketServer2 webSocketServer = webSocketMap.get(k);
            try {
                webSocketServer.sendMessage(message);
                log.info("websocket群發(fā)消息編號(hào)uid為: " + k + ",消息: " + message);
            } catch (IOException e) {
                log.error("群發(fā)自定義消息失敗: " + k + ",message: " + message);
            }
        });
    }
    /**
     * 服務(wù)端群發(fā)消息-心跳包
     * @author zhengfuping
     * @date 2023/8/22 10:09
     * @param message 推送數(shù)據(jù)
     * @return int 連接數(shù)
     */
    public static synchronized int sendPing(String message){
        if (webSocketMap.size() == 0)
            return 0;
        StringBuffer uids = new StringBuffer();
        AtomicInteger count = new AtomicInteger();
        webSocketMap.forEach((uid,server)->{
            count.getAndIncrement();

            if (webSocketMap.containsKey(uid)){
                WebSocketServer2 webSocketServer = webSocketMap.get(uid);
                try {
                    if (Integer.valueOf(uid) ==101){
                        Integer i=1/0;
                    }

                    webSocketServer.sendMessage(message);
                    if (count.equals(webSocketMap.size() - 1)){
                        uids.append("uid");
                        return;

                    }
                    uids.append(uid).append(",");
                } catch (Exception e) {
                    webSocketMap.remove(uid);
                    log.info("客戶(hù)端心跳檢測(cè)異常移除: " + uid + ",心跳發(fā)送失敗,已移除!");

                }
            }else {
                log.info("客戶(hù)端心跳檢測(cè)異常不存在: " + uid + ",不存在!");

            }
        });
        log.info("客戶(hù)端心跳檢測(cè)結(jié)果: " + uids + "連接正在運(yùn)行");
        return webSocketMap.size();
    }
    /**
     * 連接是否存在
     * @param uid
     * @return boolean
     */
    public static boolean isConnected(String uid) {
        if (Objects.nonNull(webSocketMap) && webSocketMap.containsKey(uid)) {
            return true;
        } else {
            return false;
        }
    }
}

2.4、Controller用于測(cè)試主動(dòng)發(fā)送消息

@RestController
@RequestMapping("/test")
public class WebSocketController{
    /**
     * 檢驗(yàn)連接
     * @date 2023/8/22
     * @Param * @param webSocketId
     * @return * @return String
     */
    @GetMapping("/webSocketIsConnect/{webSocketId}")
    public String webSocketIsConnect(@PathVariable("webSocketId") String webSocketId){
        if (WebSocketServer2.isConnected(webSocketId)) {
            return webSocketId+"正在連接";
        }
        return webSocketId+"連接斷開(kāi)!";
    }

    /**
     * 單發(fā) 消息
     * @author zhengfuping
     * @date 2023/8/22 10:25
     * @param webSocketId  指定 連接
     * @param message  數(shù)據(jù)
     * @param pwd 驗(yàn)證密碼
     * @return String
     */
    @GetMapping("/sendMessageByWayBillId")
    public String sendMessageByWayBillId(String webSocketId, String message, String pwd) {
        boolean flag = false;

            flag = WebSocketServer2.sendMessageByWayBillId(webSocketId, message);

        if (flag) {
            return "發(fā)送成功!";
        }
        return "發(fā)送失?。?;
    }

    /**
     * 群發(fā)
     * @author zhengfuping
     * @date 2023/8/22 10:26
     * @param message
     * @param pwd
     */
    @GetMapping("/broadSendInfo")
    public void sendInfo(String message, String pwd) {
            WebSocketServer2.sendInfo(message);
    }
}

2.5、配置定時(shí)任務(wù),用于調(diào)用主動(dòng)向客戶(hù)端發(fā)送心跳

每10秒調(diào)用一次,主動(dòng)檢測(cè),查看客戶(hù)端連接是否異常斷開(kāi),如果異常斷開(kāi),則把該會(huì)話從集合中剔除掉,避免無(wú)限積壓。

@Component
@Slf4j
public class WebSocketTask {
    
    @Scheduled(cron = "0/10 * * * * ?")
    public void clearOrders(){
        int num = 0;
        try {
            num = WebSocketServer2.sendPing("conn_success");
        } finally {
            log.info("websocket心跳檢測(cè)結(jié)果,共【" + num + "】個(gè)連接");
        }
    }
}

三、測(cè)試

1、 測(cè)試消息發(fā)送

1.1、前端日志

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

1.2、后端日志

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

2、測(cè)試客戶(hù)端異常斷開(kāi),服務(wù)器通過(guò)心跳檢測(cè)自動(dòng)剔除掉異常對(duì)話。

因?yàn)闇y(cè)試不方便,只能通過(guò)斷點(diǎn)實(shí)現(xiàn)效果

  1. 前端需要把主動(dòng)關(guān)閉會(huì)話的注釋掉,不讓主動(dòng)關(guān)閉
    前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

  2. 先在連接關(guān)閉的地方和心跳檢測(cè)地方打上斷點(diǎn),斷點(diǎn)需要設(shè)置成Thread,要不然沒(méi)法異步
    前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議

  3. 然后關(guān)閉掉一個(gè)前端頁(yè)面讓他把會(huì)話關(guān)閉,就會(huì)進(jìn)入該斷點(diǎn)位置,通過(guò)斷點(diǎn)讓它停住不讓他去正常關(guān)閉

前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議4. 然后選擇執(zhí)行該心跳檢測(cè)的斷點(diǎn)代碼
前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議
5. 進(jìn)入心跳的循環(huán)給每個(gè)會(huì)話發(fā)送心跳檢測(cè),此時(shí)前端已經(jīng)異常斷開(kāi)了
前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議
6. 因?yàn)榍岸艘呀?jīng)關(guān)閉會(huì)話了,則發(fā)送心跳會(huì)失敗,會(huì)直接進(jìn)入catch塊,然后把該會(huì)話從集合中剔除掉
前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議
最終日志
前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè)),IO通訊,前端,spring boot,后端,websocket,通訊協(xié)議文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-666179.html

到了這里,關(guān)于前端加springboot實(shí)現(xiàn)Web Socket連接通訊以及測(cè)試流程(包括后端實(shí)現(xiàn)心跳檢測(cè))的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包