【springboot】【socket】spring boot整合socket,實現服務器端兩種消息推送


 

========================前后台分別要做的事情========================

一.前台 使用js【HTML頁面】

var websocket = new WebSocket("ws://localhost:8080/echo");

獲取socket連接。

 


注意1:

  URL地址是"ws://后台服務IP:Port/路由地址"

注意2:

  路由地址是任意自定義的。后台會有配置關於一個路由地址的【Handler處理器】和【Interceptor攔截器】

注意3:

  一種路由地址之間,是可以相互通信的。

例如:

  html1 中配置路由地址:echo html2 中也配置路由地址:echo
  在服務器配置了 這一種路由地址echo的【Handler處理器】和【Interceptor攔截器】后。

  這就代表,html1和html2都在服務器上注冊了關於 路由地址echo 的webSocket服務。

  現在,只要html1使用echo注冊的webSocket服務發送消息(websocket.send(json);),服務器端就會攔截到消息,並對其他注冊了echo這個webSocket服務的頁面發送信息。

  可以是對單點發送,也可以是對所有廣播。

 

 

二.后台服務器端 spring boot 集成 webSocket

后台服務器端,需要做三件事情:


  1.配置webSocket的攔截器,實現HandshakeInterceptor接口

    (攔截和webSocket相關的信息)

  2.配置webSocket的處理器,實現WebSocketHandler接口

    (處理對一個路由地址的相關操作,例如 監聽socket連接成功,socket關閉連接,處理信息等)

  3.完善webSocket的配置,實現WebSocketConfigurer接口

    (將處理器handler 和 一個路由地址 綁定,並對本地址設置攔截器Interceptor,最后設置訪問域名的限制)

 

 

 

 

=====================spring boot 集成 webSocket===================

一.新建一個空的spring boot項目,項目依賴2.0.5 spring boot版本

【完整項目結構在最下方可見】

 

二.完善代碼

1.pom文件完整示例

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sxd</groupId>
    <artifactId>socket-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>socket-demo</name>
    <description>Demo project for Spring Boot And Socket</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

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

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

        <!--需要添加的jar 依賴-->
        <!-- json工具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>LATEST</version>
        </dependency>

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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>compile</scope>
        </dependency>


    </dependencies>




    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
View Code

 

2.新建握手攔截器

package com.sxd.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 實現【握手攔截器】
 *
 * @Slf4j       注解使用解析 https://www.cnblogs.com/sxdcgaq8080/p/11288213.html
 * @Component   注解是將本攔截器注入為Bean給Spring容器管理
 *
 * @author sxd
 * @date 2019/8/2 13:47
 */
@Slf4j
@Component
public class MyHandshakeInterceptor implements HandshakeInterceptor{

    /**
     * 重寫方法 在握手之前做的事情
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {

        if (serverHttpRequest instanceof ServletServerHttpRequest) {
            HttpServletRequest servletRequest = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest();
            // 從session中獲取到當前登錄的用戶信息. 作為socket的賬號信息. session的的WEBSOCKET_USERNAME信息,在用戶打開頁面的時候設置.
            String userName = (String) servletRequest.getSession().getAttribute("WEBSOCKET_USERNAME");
            map.put("WEBSOCKET_USERNAME", userName);
        }
        return true;
    }

    /**
     * 重寫方法 在握手之后做的事情
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @param webSocketHandler
     * @param e
     */
    @Override
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {

    }
}
View Code

 

3.新建webSocket的handler處理器

package com.sxd.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 實現WebSocketHandler接口
 *
 *
 * @author sxd
 * @date 2019/8/2 14:06
 */
@Slf4j
@Component
public class MySocketHander implements WebSocketHandler {


    /**
     * 為了保存在線用戶信息,在方法中新建一個list存儲一下【實際項目依據復雜度,可以存儲到數據庫或者緩存】
     */
    private final static List<WebSocketSession> SESSIONS = Collections.synchronizedList(new ArrayList<>());



    @Override
    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
        log.info("鏈接成功......");
        SESSIONS.add(webSocketSession);
        String userName = (String) webSocketSession.getAttributes().get("WEBSOCKET_USERNAME");
        if (userName != null) {
            JSONObject obj = new JSONObject();
            // 統計一下當前登錄系統的用戶有多少個
            obj.put("count", SESSIONS.size());
            users(obj);
            webSocketSession.sendMessage(new TextMessage(obj.toJSONString()));
        }
    }

    @Override
    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
        log.info("處理要發送的消息");
        JSONObject msg = JSON.parseObject(webSocketMessage.getPayload().toString());
        JSONObject obj = new JSONObject();
        if (msg.getInteger("type") == 1) {
            //給所有人
            obj.put("msg", msg.getString("msg"));
            sendMessageToUsers(new TextMessage(obj.toJSONString()));
        } else {
            //給個人
            String to = msg.getString("to");
            obj.put("msg", msg.getString("msg"));
            sendMessageToUser(to, new TextMessage(obj.toJSONString()));
        }
    }

    @Override
    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
        if (webSocketSession.isOpen()) {
            webSocketSession.close();
        }
        log.info("鏈接出錯,關閉鏈接......");
        SESSIONS.remove(webSocketSession);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
        log.info("鏈接關閉......" + closeStatus.toString());
        SESSIONS.remove(webSocketSession);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }


    /**
     * 給某個用戶發送消息
     *
     * @param userName
     * @param message
     */
    public void sendMessageToUser(String userName, TextMessage message) {

        log.info("發消息給某個用戶");
        for (WebSocketSession user : SESSIONS) {
            if (user.getAttributes().get("WEBSOCKET_USERNAME").equals(userName)) {
                try {
                    if (user.isOpen()) {
                        user.sendMessage(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }

    /**
     * 給所有在線用戶發送消息
     *
     * @param message
     */
    public void sendMessageToUsers(TextMessage message) {
        log.info("發送消息給所用用戶");
        for (WebSocketSession user : SESSIONS) {
            try {
                if (user.isOpen()) {
                    user.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 將系統中的用戶傳送到前端
     *
     * @param obj
     */
    private void users(JSONObject obj) {
        List<String> userNames = new ArrayList<>();
        for (WebSocketSession webSocketSession : SESSIONS) {
            userNames.add((String) webSocketSession.getAttributes().get("WEBSOCKET_USERNAME"));
        }
        obj.put("users", userNames);
    }


}
View Code

 

4.新建webSocket的config配置

package com.sxd.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * @author sxd
 * @date 2019/8/2 14:43
 */
@Slf4j
@Configuration
@EnableWebSocket
public class MyWebSocketConfig  implements WebSocketConfigurer {

    @Autowired
    MyHandshakeInterceptor handshakeInterceptor;

    @Autowired
    MySocketHander socketHander;




    /**
     * 實現 WebSocketConfigurer 接口
     * 重寫 registerWebSocketHandlers 方法,這是一個核心實現方法,配置 websocket 入口,允許訪問的域、注冊 Handler、SockJs 支持和攔截器。
     *
     * registry.addHandler()注冊和路由的功能
     *      當客戶端發起 websocket 連接,把 /path 交給對應的 handler 處理,而不實現具體的業務邏輯,可以理解為收集和任務分發中心。
     *
     *
     * addInterceptors() 顧名思義就是為 handler 添加攔截器
     *      可以在調用 handler 前后加入我們自己的邏輯代碼。
     *
     *
     * setAllowedOrigins(String[] domains),允許指定的域名或 IP (含端口號)建立長連接
     *      如果只允許自家域名訪問,這里輕松設置。如果不限時使用 * 號,如果指定了域名,則必須要以 http 或 https 開頭。
     *
     * @param registry
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //部分 支持websocket 的訪問鏈接,允許跨域
        registry.addHandler(socketHander, "/my_SXD_Socket").addInterceptors(handshakeInterceptor).setAllowedOrigins("*");
        //部分 不支持websocket的訪問鏈接,允許跨域
        registry.addHandler(socketHander, "/sockjs/my_SXD_Socket").addInterceptors(handshakeInterceptor).setAllowedOrigins("*").withSockJS();
    }
}
View Code

 

5.新建controller用於模擬用戶登錄,給Session填充值

package com.sxd.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

/**
 * 本controller中的三個方法,即相當於一個login接口的 三次請求,只不過值寫死在方法中。
 *
 * 只需要分別請求三個接口,即相當於三個用戶分別作了登錄操作,並存儲到了session中。
 *
 * @author sxd
 * @date 2019/8/2 14:48
 */
@Controller
@RequestMapping("/socket")
public class LoginController {




    /**
     * 第一個用戶
     *
     * @param request
     * @return
     */
    @RequestMapping("/chat1")
    public String chat1(HttpServletRequest request) {
        // 假設用戶tom登錄,存儲到session中
        request.getSession().setAttribute("WEBSOCKET_USERNAME", "tom");
        return "chat1";
    }

    /**
     * 第二個用戶登錄
     *
     * @param request
     * @return
     */
    @RequestMapping("/chat2")
    public String chat2(HttpServletRequest request) {
        // 假設用戶jerry登錄,存儲到session中
        request.getSession().setAttribute("WEBSOCKET_USERNAME", "jerry");
        return "chat2";
    }

    /**
     * 第三個用戶登錄
     *
     * @param request
     * @return
     */
    @RequestMapping("/chat3")
    public String chat3(HttpServletRequest request) {
        // 假設用戶jack登錄,存儲到session中
        request.getSession().setAttribute("WEBSOCKET_USERNAME", "jack");
        return "chat3";
    }

}
View Code

 

6.完善application.properties項目配置,全是freemarker的配置

spring.freemarker.template-loader-path=classpath:/templates
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.request-context-attribute=request
spring.freemarker.suffix=.html
View Code

 

7.新建一個templates目錄在resource下,並復制下面三份,分別在templates下創建chat1.html、chat2.html、chat3.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>測試websocket</title>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.css">
</head>
<body>
<div class="container">
    <div class="input-group mb-3">
        <div class="input-group-prepend">
            <label class="input-group-text" for="inputGroupSelect01">用戶</label>
        </div>
        <select class="custom-select" id="inputGroupSelect01">
            <option selected>選擇一個...</option>
        </select>
    </div>
    <div class="input-group mb-3">
        <input type="text" class="form-control">
        <div class="input-group-append">
            <span class="input-group-text" id="btn1">發送給所有人</span>
        </div>
    </div>
    <div class="input-group mb-3">
        <input type="text" class="form-control">
        <div class="input-group-append">
            <span class="input-group-text" id="btn2">發送給單人</span>
        </div>
    </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.2/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.js"></script>
<script language=javascript>
    $(function () {
        var websocket;
        if ('WebSocket' in window) {
            console.log("WebSocket-->");
            //new WebSocket()在IDEA打開的任何項目中都可以 直接調用
            websocket = new WebSocket("ws://localhost:8080/my_SXD_Socket");
        } else if ('MozWebSocket' in window) {
            console.log("MozWebSocket-->");
            websocket = new MozWebSocket("ws://my_SXD_Socket");
        } else {
            console.log("SockJS-->");
            websocket = new SockJS("http://127.0.0.1:8080/sockjs/my_SXD_Socket");
        }


        websocket.onopen = function (evnt) {
            console.log("鏈接服務器成功!", evnt.data);
        };
        websocket.onmessage = function (evnt) {
            console.log('收到消息:', evnt.data);
            var json = JSON.parse(evnt.data);
            if (json.hasOwnProperty('users')) {
                var users = json.users;
                for (var i = 0; i < users.length; i++) {
                    $("#inputGroupSelect01").append('<option value="' + users[i] + '">' + users[i] + '</option>');
                }
            } else {
                //打印消息
                toast(json.msg, 'info')
            }
        };
        websocket.onerror = function (evnt) {
        };
        websocket.onclose = function (evnt) {
            console.log("與服務器斷開了鏈接!")
        }
        $('#btn2').bind('click', function () {
            if (websocket != null) {
                //根據勾選的人數確定是群聊還是單聊
                var value = $(this).parent().parent().find('input').val();
                //得到選擇的用戶
                var name = $("#inputGroupSelect01").find("option:selected").val();
                console.log('選中的用戶', name);
                if (name === '選擇一個...') {
                    toast('請選擇一個用戶', 'warning')
                } else {
                    var object = {
                        to: name,
                        msg: value,
                        type: 2
                    };
                    //將object轉成json字符串發送給服務端
                    var json = JSON.stringify(object);
                    websocket.send(json);
                }
            } else {
                console.log('未與服務器鏈接.');
            }
        });
        $('#btn1').bind('click', function () {
            if (websocket != null) {
                //根據勾選的人數確定是群聊還是單聊
                var value = $(this).parent().parent().find('input').val();
                var object = {
                    msg: value,
                    type: 1
                };
                //將object轉成json字符串發送給服務端
                var json = JSON.stringify(object);
                websocket.send(json);
            } else {
                console.log('未與服務器鏈接.');
            }
        });
    })

    function toast(text, icon) {
        $.toast({
            text: text,
            heading: '新消息',
            icon: icon,
            showHideTransition: 'slide',
            allowToastClose: true,
            hideAfter: 3000,
            stack: 5,
            position: 'top-right',

            bgColor: '#444444',
            textColor: '#eeeeee',
            textAlign: 'left',
            loader: true,
            loaderBg: '#006eff'
        });
    }
</script>
</body>
</html>
View Code

 

8.最終項目結構如下:

 

 

9.啟動項目,並分別訪問下面三個URL,模擬用戶登錄了

http://localhost:8080/socket/chat1

http://localhost:8080/socket/chat2

http://localhost:8080/socket/chat3

 

 

 

 10.發送消息給單人或多人

 

發送給指定的人:

 

 

 

發送給所有人:

 

 

 

 

參考地址:https://www.cnblogs.com/hhhshct/p/8849449.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM