spring boot +WebSocket 廣播式實(shí)例

1. 項(xiàng)目介紹

  • 客戶端頁面index.html可以輸入消息鸥滨,點(diǎn)擊發(fā)送弦叶,通過ws協(xié)議發(fā)送到服務(wù)器端科盛;
  • 服務(wù)器端收到客戶端消息后,廣播給各個(gè)客戶端壳快;
  • 服務(wù)器端頁面server.html纸巷,可以輸入消息,點(diǎn)擊發(fā)送眶痰,通過ws雙工通信推送給客戶端瘤旨;
  • 客戶端有顯示服務(wù)器端廣播消息的區(qū)域。

2. 項(xiàng)目目錄結(jié)構(gòu)

3. 項(xiàng)目代碼分析

3.1 pom.xml文件添加必要依賴

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

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <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-test</artifactId>
            <scope>test</scope>
        </dependency>

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

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

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>bootstrap</artifactId>
            <version>3.3.7-1</version>
        </dependency>
    </dependencies>

3.2 .java代碼

  • 新建WebSocket 的配置類WebSocketConfig.java
package com.springboot.websocket.websocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

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

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

  • WebSocket的服務(wù)端
package com.springboot.websocket.websocket.config;

import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * WebSocket的服務(wù)端
 * 因?yàn)閃ebSocket是類似客戶端服務(wù)端的形式(采用ws協(xié)議)竖伯,
 * 那么這里的WebSocketServer其實(shí)就相當(dāng)于一個(gè)ws協(xié)議的Controller
 * 直接@ServerEndpoint(“/websocket”)@Component啟用即可存哲,
 * 然后在里面實(shí)現(xiàn)@OnOpen,@onClose,@onMessage等方法
 */
@ServerEndpoint("/websocket")
@Component
public class WebSocketServer {

    //靜態(tài)變量因宇,用來記錄當(dāng)前在線連接數(shù)。應(yīng)該把它設(shè)計(jì)成線程安全的祟偷∮鸬眨可選的
    private static int onlineCount = 0;
    //concurrent包的線程安全Set,用來存放每個(gè)客戶端對(duì)應(yīng)的MyWebSocket對(duì)象肩袍。必須要有的
    private static CopyOnWriteArraySet<WebSocketServer>
            webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    //與某個(gè)客戶端的連接會(huì)話杭棵,需要通過它來給客戶端發(fā)送數(shù)據(jù),必須
    private Session session;

    /**
     * 連接建立成功調(diào)用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在線數(shù)加1
        //一旦連接成功后就在控制臺(tái)打印信息
        System.out.println("有新窗口開始監(jiān)聽,當(dāng)前在線人數(shù)為" + getOnlineCount());
        try {
            //往客戶端發(fā)送消息
            sendMessage("連接成功");
        } catch (IOException e) {
            System.out.println("WebSocket IO異常");
        }
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //從set中刪除
        subOnlineCount();           //在線數(shù)減1
        System.out.println("有連接關(guān)閉氛赐!當(dāng)前在線人數(shù)為" + getOnlineCount());
    }

    /**
     * 收到客戶端消息后調(diào)用的方法
     *
     * @param message 客戶端發(fā)送過來的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //message 是從客戶端發(fā)送過來的消息魂爪,并在控制臺(tái)打印
        System.out.println("收到客戶端的信息:" + message);
        //群發(fā)消息
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("發(fā)生錯(cuò)誤");
        error.printStackTrace();
    }

    /**
     * 實(shí)現(xiàn)服務(wù)器主動(dòng)推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群發(fā)自定義消息
     */
    public static void sendInfo(String message) throws IOException {
        System.out.println("推送消息內(nèi)容:" + message);
        for (WebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }
}

  • WebSocketController.java 控制層代碼
package com.springboot.websocket.websocket.controller;

import com.springboot.websocket.websocket.config.WebSocketServer;
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.RequestParam;

import java.io.IOException;

/**

 * WebSocketController
 */
@Controller
@RequestMapping("/socket")
public class WebSocketController {


    /**
     * 輸入消息的頁面
     * @return
     */
    @GetMapping("/send")
    public String sendMsg(){
        return "server";
    }

    /**
     * 推送數(shù)據(jù)接口
     * @Param message
     * @return
     */
    @RequestMapping("/push")
    public String pushMsg(@RequestParam("message")String message) {
        try {
            WebSocketServer.sendInfo(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "server";
    }
}

3.3 html頁面代碼

  • 主頁面index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket</title>
</head>
<body>
<h1>WebScoket練習(xí)</h1>
<label>
    <input id="msg" type="text" name="message" style="width:215px;" />
    <input type="submit" id="btn" />
</label>
<br>


<div>
<ul id="message_show_ul">
</ul>
</div>
<script language="JavaScript">
    var send_message = document.getElementById("msg");
    var sub_button = document.getElementById("btn");
    var show_ul = document.getElementById("message_show_ul");
    var socket;
    if (typeof(WebSocket) == "undefined") {
        console.log("您的瀏覽器不支持WebSocket");
    } else {
        console.log("您的瀏覽器支持WebSocket");
        //實(shí)現(xiàn)化WebSocket對(duì)象,指定要連接的服務(wù)器地址與端口建立連接
        socket = new WebSocket("ws://localhost:8088/websocket");

        //打開事件
        socket.onopen = function () {
            console.log("Socket已打開");
            sub_button.onclick = function () {
                console.log(send_message);
                //下面這句話是往服務(wù)器發(fā)送消息
                socket.send("這是來自客戶端的消息:" + send_message.value);
            };

        };

        //獲得消息事件艰管,從服務(wù)器獲得消息
        socket.onmessage = function (msg) {

            var li_eme = document.createElement("li");
            li_eme.innerHTML = msg.data;
            show_ul.appendChild(li_eme);
            //控制臺(tái)打印消息
            console.log(msg.data);
            //彈出框彈出消息
           // alert(msg.data);
        };

        //關(guān)閉事件
        socket.onclose = function () {
            console.log("Socket已關(guān)閉");
        };

        //發(fā)生了錯(cuò)誤事件
        socket.onerror = function () {
            alert("Socket發(fā)生了錯(cuò)誤");
        }
    }
</script>
</body>
</html>
  • 服務(wù)器端頁面server.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocketServer</title>
</head>
<body>
<h1>服務(wù)器端</h1>
<form id="login" action="/socket/push" method="get">
    <label>
        <input id="msg" type="text" name="message" style="width:215px;" />
        <input type="submit" id="btn"/>
    </label>
    <br>
</form>
</body>
</html>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滓侍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子牲芋,更是在濱河造成了極大的恐慌撩笆,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缸浦,死亡現(xiàn)場(chǎng)離奇詭異夕冲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)裂逐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門歹鱼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人卜高,你說我怎么就攤上這事弥姻。” “怎么了掺涛?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵庭敦,是天一觀的道長。 經(jīng)常有香客問我薪缆,道長秧廉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任矮燎,我火速辦了婚禮定血,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诞外。我一直安慰自己呜呐,他們只是感情好矾兜,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布此改。 她就那樣靜靜地躺著,像睡著了一般刊苍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上濒析,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天正什,我揣著相機(jī)與錄音,去河邊找鬼号杏。 笑死婴氮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盾致。 我是一名探鬼主播主经,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼庭惜!你這毒婦竟也來了罩驻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤护赊,失蹤者是張志新(化名)和其女友劉穎惠遏,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體骏啰,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡节吮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了器一。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片课锌。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖祈秕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情雏胃,我是刑警寧澤请毛,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站瞭亮,受9級(jí)特大地震影響方仿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜统翩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一仙蚜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厂汗,春花似錦委粉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汁汗。三九已至,卻和暖如春栗涂,著一層夾襖步出監(jiān)牢的瞬間知牌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國打工斤程, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留角寸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓忿墅,卻偏偏與公主長得像扁藕,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子球匕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架纹磺,建立于...
    Hsinwong閱讀 22,435評(píng)論 1 92
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)亮曹,斷路器橄杨,智...
    卡卡羅2017閱讀 134,702評(píng)論 18 139
  • 原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-WebSo...
    敢夢(mèng)敢當(dāng)閱讀 8,918評(píng)論 0 50
  • mark今天,來日本出差一個(gè)月了照卦,想家啊式矫,跟妞在一起的時(shí)候有意思 今天去龜戶買菜,經(jīng)過地鐵路口時(shí)役耕,剛好有地鐵路過采转,...
    Deanbian閱讀 143評(píng)論 2 0
  • 付費(fèi)課程報(bào)了一大堆、買書的也不少瞬痘、看的公眾號(hào)也挺多的故慈,到頭來沒學(xué)到點(diǎn)什么,還是陷在知識(shí)焦慮中無法逃脫框全,在這時(shí)候不如...
    1astsummer閱讀 381評(píng)論 5 2