springboot接入websocket

一. 導(dǎo)入依賴

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

二. 開啟套接字

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

/**
 * @author lhh
 */
@Configuration
public class WebSocketConfig {

    /**
     * 開啟WebSocket功能
     * @return 消息點(diǎn)
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

三. 核心代碼

package com.lhh.demo.im;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

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

@Slf4j
@Component
@ServerEndpoint("/websocket/{id}")
public class WebSocketServer {

    /**
     * 客戶端ID
     */
    private String id = "";

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

    /**
     * 記錄當(dāng)前在線連接數(shù)(為保證線程安全琅绅,須對(duì)使用此變量的方法加lock或synchronized)
     */
    private static int onlineCount = 0;

    /**
     * 用來存儲(chǔ)當(dāng)前在線的客戶端(此map線程安全)
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 連接建立成功后調(diào)用
     */
    @OnOpen
    public void onOpen(@PathParam(value = "id") String id, Session session) {
        this.session = session;
        // 接收到發(fā)送消息的客戶端編號(hào)
        this.id = id;
        // 加入map中
        webSocketMap.put(id, this);
        // 在線數(shù)加1
        addOnlineCount();
        log.info("客戶端" + id + "加入,當(dāng)前在線數(shù)為:" + getOnlineCount());
        try {
            sendMessage("WebSocket連接成功");
        } catch (IOException e) {
            log.error("WebSocket IO異常");
        }
    }

    /**
     * 連接關(guān)閉時(shí)調(diào)用
     */
    @OnClose
    public void onClose() {
        // 從map中刪除
        webSocketMap.remove(this);
        // 在線數(shù)減1
        subOnlineCount();
        log.info("有一連接關(guān)閉灾杰,當(dāng)前在線數(shù)為:" + getOnlineCount());
    }

    /**
     * 收到客戶端消息后調(diào)用
     * @param message 客戶端發(fā)送過來的消息<br/>
     *                消息格式:內(nèi)容 - 表示群發(fā)般卑,內(nèi)容|X - 表示發(fā)給id為X的客戶端
     * @param session 用戶信息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("來自客戶端的消息:" + message);
        String[] messages = message.split("[|]");
        try {
            if (messages.length > 1) {
                sendToUser(messages[0], messages[1]);
            } else {
                sendToAll(messages[0]);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 發(fā)生錯(cuò)誤時(shí)回調(diào)
     *
     * @param session 用戶信息
     * @param error 錯(cuò)誤
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket發(fā)生錯(cuò)誤");
        error.printStackTrace();
    }

    /**
     * 推送信息給指定ID客戶端逢艘,如客戶端不在線哨颂,則返回不在線信息給自己
     *
     * @param message 客戶端發(fā)來的消息
     * @param sendClientId 客戶端ID
     */
    public void sendToUser(String message, String sendClientId) throws IOException {
        if (webSocketMap.get(sendClientId) != null) {
            if (!id.equals(sendClientId)) {
                webSocketMap.get(sendClientId)
                    .sendMessage("客戶端" + id + "發(fā)來消息:" + " <br/> " + message);
            } else {
                webSocketMap.get(sendClientId).sendMessage(message);
            }
        } else {
            // 如客戶端不在線瓷患,則返回不在線信息給自己
            sendToUser("當(dāng)前客戶端不在線", id);
        }
    }

    /**
     * 群送發(fā)送信息給所有人
     *
     * @param message 要發(fā)送的消息
     */
    public void sendToAll(String message) throws IOException {
        for (String key : webSocketMap.keySet()) {
            webSocketMap.get(key).sendMessage(message);
        }
    }

    /**
     * 發(fā)送消息
     * @param message 要發(fā)送的消息
     */
    private void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 獲取在線人數(shù)
     * @return 在線人數(shù)
     */
    private static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 有人上線時(shí)在線人數(shù)加一
     */
    private static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    /**
     * 有人下線時(shí)在線人數(shù)減一
     */
    private static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

}

四. 測(cè)試

以下方法選擇其一即可:

1. 使用websocket測(cè)試網(wǎng)站 http://ws.douqq.com/(推薦純后端使用)

2. 瀏覽器安裝WebSocket Client插件

3. 前端對(duì)接

<!DOCTYPE HTML>
<html>
<head>
    <title>WebSocket測(cè)試</title>
</head>

<body>
Welcome<br/>
<input id="text" type="text" />
<button onclick="send()">發(fā)送消息</button>
<button onclick="closeWebSocket()">關(guān)閉連接</button>
<div id="message">
</div>
</body>

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

    // 判斷當(dāng)前瀏覽器是否支持WebSocket
    if('WebSocket' in window){
        // 為了方便測(cè)試,故將鏈接寫死
        websocket = new WebSocket("ws://localhost:8088/websocket/1");
    } else{
        alert('Not support websocket')
    }

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

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

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

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

    // 監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí)竹祷,主動(dòng)去關(guān)閉websocket連接谈跛,防止連接還沒斷開就關(guān)閉窗口,server端會(huì)拋異常
    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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末塑陵,一起剝皮案震驚了整個(gè)濱河市感憾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌令花,老刑警劉巖阻桅,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兼都,居然都是意外死亡嫂沉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門扮碧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趟章,“玉大人,你說我怎么就攤上這事慎王∮却В” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵柬祠,是天一觀的道長北戏。 經(jīng)常有香客問我,道長漫蛔,這世上最難降的妖魔是什么嗜愈? 我笑而不...
    開封第一講書人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮莽龟,結(jié)果婚禮上蠕嫁,老公的妹妹穿的比我還像新娘。我一直安慰自己毯盈,他們只是感情好剃毒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搂赋,像睡著了一般赘阀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脑奠,一...
    開封第一講書人閱讀 51,708評(píng)論 1 305
  • 那天基公,我揣著相機(jī)與錄音,去河邊找鬼宋欺。 笑死轰豆,一個(gè)胖子當(dāng)著我的面吹牛胰伍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播酸休,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼骂租,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了斑司?” 一聲冷哼從身側(cè)響起渗饮,我...
    開封第一講書人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陡厘,沒想到半個(gè)月后抽米,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體特占,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糙置,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了是目。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谤饭。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖懊纳,靈堂內(nèi)的尸體忽然破棺而出揉抵,到底是詐尸還是另有隱情,我是刑警寧澤嗤疯,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布冤今,位于F島的核電站,受9級(jí)特大地震影響茂缚,放射性物質(zhì)發(fā)生泄漏戏罢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一脚囊、第九天 我趴在偏房一處隱蔽的房頂上張望龟糕。 院中可真熱鬧,春花似錦悔耘、人聲如沸讲岁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缓艳。三九已至,卻和暖如春看峻,著一層夾襖步出監(jiān)牢的瞬間郎任,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來泰國打工备籽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舶治,地道東北人分井。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像霉猛,于是被迫代替她去往敵國和親尺锚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355