spring boot整合Websocket筆記

特別說明:自學(xué)筆記
使用websocket有兩種方式:

  • 使用sockjs,
  • 使用h5的標(biāo)準竹伸。

使用Html5標(biāo)準自然更方便簡單辫樱,所以記錄的是配合h5的使用方法幸斥。

1匹摇、pom.xml中添加如下:

核心是@ServerEndpoint這個注解。這個注解是Javaee標(biāo)準里的注解甲葬,tomcat7以上已經(jīng)對其進行了實現(xiàn)廊勃,如果是用傳統(tǒng)方法使用tomcat發(fā)布項目,只要在pom文件中引入javaee標(biāo)準即可使用经窖。

 <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-api</artifactId>
      <version>7.0</version>
      <scope>provided</scope>
    </dependency>

但使用springboot的內(nèi)置tomcat時坡垫,就不需要引入javaee-api了,spring-boot已經(jīng)包含了画侣。使用springboot的websocket功能首先引入springboot組件冰悠。

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

順便說一句,springboot的高級組件會自動引用基礎(chǔ)的組件配乱,像spring-boot-starter-websocket就引入了spring-boot-starter-web和spring-boot-starter溉卓,所以不要重復(fù)引入。

2搬泥、使用@ServerEndpoint創(chuàng)立websocket endpoint

首先要注入ServerEndpointExporter桑寨,這個bean會自動注冊使用了@ServerEndpoint注解聲明的Websocket endpoint。要注意忿檩,如果使用獨立的servlet容器尉尾,而不是直接使用springboot的內(nèi)置容器,就不要注入ServerEndpointExporter燥透,因為它將由容器自己提供和管理沙咏。

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

接下來就是寫websocket的具體實現(xiàn)類辨图,很簡單,直接上代碼:

package com.reapal.websocket;

import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author jackcooper
 * @create 2017-12-28 13:04
 */
@ServerEndpoint(value = "/websocket")
@Component
public class ApplicationWebSocket {
    //靜態(tài)變量芭碍,用來記錄當(dāng)前在線連接數(shù)徒役。設(shè)計成線程安全的。
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    //concurrent包的線程安全Set窖壕,用來存放每個客戶端對應(yīng)的ApplicationWebSocket對象忧勿。
    private static CopyOnWriteArraySet<ApplicationWebSocket> webSocketSet = new CopyOnWriteArraySet<ApplicationWebSocket>();

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

    /**
     * 連接建立成功調(diào)用的方法*/
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在線數(shù)加1
        System.out.println("有新連接加入瞻讽!當(dāng)前在線人數(shù)為" + getOnlineCount());
        try {
            sendMessage("當(dāng)前在線人數(shù)為:"+getOnlineCount());
        } catch (IOException e) {
            System.out.println("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) {
        System.out.println("來自客戶端的消息:" + message);

        //群發(fā)消息
        for (ApplicationWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 發(fā)生錯誤時調(diào)用
     @OnError
     */
     public void onError(Session session, Throwable error) {
     System.out.println("發(fā)生錯誤");
     error.printStackTrace();
     }


     public void sendMessage(String message) throws IOException {
         //getBasicRemote是阻塞式的
         this.session.getBasicRemote().sendText(message);
         //非阻塞式的
         // this.session.getAsyncRemote().sendText(message);
     }


     /**
      * 群發(fā)自定義消息
      * */
    public static void sendInfo(String message) throws IOException {
        for (ApplicationWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

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

    public static synchronized void addOnlineCount() {
        ApplicationWebSocket.onlineCount.getAndIncrement();
    }

    public static synchronized void subOnlineCount() {
        ApplicationWebSocket.onlineCount.getAndDecrement();
    }
}


使用springboot的唯一區(qū)別是要@Component聲明下,而使用獨立容器是由容器自己管理websocket的速勇,但在springboot中連容器都是spring管理的晌砾。

雖然@Component默認是單例模式的,但springboot還是會為每個websocket連接初始化一個bean烦磁,所以可以用一個靜態(tài)set保存起來养匈。

3、前端代碼

<!DOCTYPE HTML>
<html>
<head>
    <title>My WebSocket</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判斷當(dāng)前瀏覽器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8084/websocket");
    }
    else{
        alert('Not support websocket')
    }

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

    //連接成功建立的回調(diào)方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }

    //接收到消息的回調(diào)方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }

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

    //監(jiān)聽窗口關(guān)閉事件都伪,當(dāng)窗口關(guān)閉時呕乎,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口陨晶,server端會拋異常猬仁。
    window.onbeforeunload = function(){
        websocket.close();
    }

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

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

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

4、總結(jié)

springboot已經(jīng)做了深度的集成和優(yōu)化先誉,要注意是否添加了不需要的依賴湿刽、配置或聲明。由于很多講解組件使用的文章是和spring集成的褐耳,會有一些配置诈闺,在使用springboot時,由于springboot已經(jīng)有了自己的配置铃芦,再這些配置有可能導(dǎo)致各種各樣的異常买雾。


spring boot集成WebSocket實時輸出日志到web頁面

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市杨帽,隨后出現(xiàn)的幾起案子漓穿,更是在濱河造成了極大的恐慌,老刑警劉巖注盈,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晃危,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機僚饭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門震叮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鳍鸵,你說我怎么就攤上這事苇瓣。” “怎么了偿乖?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵击罪,是天一觀的道長。 經(jīng)常有香客問我贪薪,道長媳禁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任画切,我火速辦了婚禮竣稽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霍弹。我一直安慰自己毫别,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布典格。 她就那樣靜靜地躺著岛宦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钝计。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天齐佳,我揣著相機與錄音私恬,去河邊找鬼。 笑死炼吴,一個胖子當(dāng)著我的面吹牛本鸣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播硅蹦,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼荣德,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了童芹?” 一聲冷哼從身側(cè)響起涮瞻,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎假褪,沒想到半個月后署咽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年宁否,在試婚紗的時候發(fā)現(xiàn)自己被綠了窒升。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡慕匠,死狀恐怖饱须,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情台谊,我是刑警寧澤蓉媳,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站青伤,受9級特大地震影響督怜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狠角,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一号杠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧丰歌,春花似錦姨蟋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晓勇,卻和暖如春堂飞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绑咱。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工绰筛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人描融。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓铝噩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窿克。 傳聞我的和親對象是個殘疾皇子骏庸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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