Java輕鬆搭建簡易WebSocket,附可用源碼

前言

最近,本人在自學WebSocket,然而在百度學習的過程中,發現要麼按照文章的代碼搭起來的WebSocket不能用,要麼太過複雜,看的本菜鳥一愣一愣的,不知從何入手。

所幸有大佬賜教,本人終於實現了一個簡易的WebSocket,如圖:

第二張圖:

 

界面與內部確實有些簡陋,不過請大家放心,確實是WebSocket實現的。

接下來就進入正題:

 

WebSocket作用

簡單的說,WebSocket可以讓服務器直接給客戶端發送信息,而不是先等客戶端發起請求後、服務器才返回信息。(比起輪詢,能更好的節省服務器資源和帶寬,並且能夠更實時地進行通訊。關於具體實現原理在此不再贅述,可查看相關專業文章。)

 

Java搭建WebSocket

1.使用IDEA創建SpringBoot項目

由於本人是用springboot實現的,因此這麼寫;當然也可以用spring框架之類的。

2.創建一個WebSocket類

用來接收客戶端的webSocket鏈接請求、處理主要邏輯,代碼如下:

package com.websocket.demo.controller;

import net.sf.json.JSONObject;
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.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/webSocket/{username}")

public class WebSocket {
    //@ServerEndpoint("/webSocket/{username}")
    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
    private static int onlineCount = 0;
    private Session session;
    private String username;

    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) throws IOException {
        this.username = username;
        this.session = session;
        addOnlineCount();
        clients.put(username, this);
        System.out.println("已連接");

    }

    @OnClose
    public void onClose() throws IOException {
        clients.remove(username);
        subOnlineCount();
    }



    @OnMessage
    public void onMessage(String message) throws IOException {
        JSONObject jsonTo = JSONObject.fromObject(message);
        String mes = (String) jsonTo.get("message");
        if (!jsonTo.get("To").equals("All")){
            sendMessageTo(mes, jsonTo.get("To").toString());
        }else{
            sendMessageAll("給所有人");
        }
    }



    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }



    public void sendMessage(String message) throws IOException {
        // session.getBasicRemote().sendText(message);
        session.getAsyncRemote().sendText(message);

    }



    public void sendMessageTo(String message, String To) throws IOException {
        // session.getBasicRemote().sendText(message);
        // session.getAsyncRemote().sendText(message);
        for (WebSocket item : clients.values()) {
            if (item.username.equals(To) )
                item.session.getAsyncRemote().sendText(message);
        }
    }



    public void sendMessageAll(String message) throws IOException {
        for (WebSocket item : clients.values()) {
            item.session.getAsyncRemote().sendText(message);
        }
    }



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



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



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



    public static synchronized Map<String, WebSocket> getClients() {
        return clients;
    }
}

3.重要!創建一個WebSocketConfig類

這個類非常重要,如果沒有這個類,就無法建立WebSocket鏈接(本人當時就是卡在這一步了,都不知道爲什麼無法建立鏈接,網上也不說明白,這讓本菜鳥怎麼搞;多虧大佬賜教才明白),代碼如下:

package com.websocket.demo.controller;

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

@Configuration
public class WebSocketConfig {
    /**
     * ServerEndpointExporter 作用
     *
     * 這個Bean會自動註冊使用@ServerEndpoint註解聲明的websocket endpoint
     *
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

4.創建一個ServerController類

這個類是模擬服務器給客戶端發送信息用的,通過WebSocket,服務器就可以直接給客戶端發送信息。注意,爲了便於測試,user變量寫死了。代碼如下:

package com.websocket.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.websocket.Session;
import java.io.IOException;

@RestController
public class ServerController {
    @RequestMapping(value = "/server",method={RequestMethod.POST, RequestMethod.GET})
    public String server(HttpServletRequest request) throws IOException {
        try {
            String msg = request.getParameter("msg");
            String user = request.getParameter("user");
            //注意,爲了便於測試,這裏寫死了
            user = "abc";
            //獲取用戶的webSocket對象
            WebSocket ws = WebSocket.getClients().get(user);
            //發送消息
            ws.sendMessage(msg);
        }catch (Exception e){
            System.out.println(e.toString());
        }
        return "<script language=\"javascript\" type=\"text/javascript\">\n" +
                "window.location.href=\"server.html\";\n" +
                "</script>";
    }
}

5.修改index.html文件

創建springboot項目時,idea會自動生成一個index.html文件(在resources/static下),因此只要修改即可,代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="index.js" type="text/javascript"></script>
    <meta charset="UTF-8">
    <title>客戶端</title>
    <base target="_blank">
</head>
<body>
<h1>客戶端</h1><br/>
<a href="server.html" target="_blank">打開服務器頁面</a>
<button onclick="buttonCreate()">與服務器建立websocket鏈接</button>
<button onclick="buttonClose()">關閉websocket鏈接</button>
<p>鏈接狀態:</p>
<h1 id="status">未建立鏈接</h1>

<p>從服務器發來的信息:</p>
<h1 id="message"></h1>
</body>
</html>

6.創建index.js文件

這個文件放在index.html旁邊即可,是客戶端申請建立WebSocket鏈接用的,也比較重要。注意,爲了便於測試,username變量寫死了。代碼如下:

var websocket = null;

var host = document.location.host;

var username = "${loginUsername}"; // 獲得當前登錄人員的userName

// alert(username)

//判斷當前瀏覽器是否支持WebSocket
if ('WebSocket' in window) {
    alert("瀏覽器支持Websocket")
    //假設當前用戶是abc
    username = "abc";
    //alert(username);
    //alert('ws://'+host+'/webSocket/'+username);
} else {
    alert('當前瀏覽器 Not support websocket')
}


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

//建立websocket鏈接
function buttonCreate() {
    try {
        websocket = new WebSocket('ws://' + host + '/webSocket/' + username);
        initWebSocket();
    }catch (e){
        alert(e);
    }
}

//關閉websocket鏈接
function buttonClose() {
   try{
    websocket.close();
   }catch (e){
    alert(e)
   }
}

function initWebSocket() {

    //連接發生錯誤的回調方法
    websocket.onerror = function() {
        //alert("WebSocket連接發生錯誤")
        setMessageInnerHTML("WebSocket連接發生錯誤");

    };



    //連接成功建立的回調方法
    websocket.onopen = function() {
        //alert("WebSocket連接成功")
        changeStatus("WebSocket連接成功");
    }



    //接收到消息的回調方法
    websocket.onmessage = function(event) {
        //alert("這是後臺推送的消息:"+event.data);
        setMessageInnerHTML(event.data);
    }



    //連接關閉的回調方法
    websocket.onclose = function() {
        changeStatus("WebSocket連接關閉");
    }



    //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
    window.onbeforeunload = function() {
        try {
            websocket.close();
        }catch (e){
            alert(e);
        }
    }
}

function changeStatus(text) {
    document.getElementById("status").innerText = text;
}

7.創建server.html文件

這個文件是模擬服務器頁面用的。代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>服務器端</title>
</head>
<body>
<h1>服務器端</h1>
<p>給客戶端發送信息:</p>
<form method="post" action="/server" target="nm_iframe">
    <label>目標用戶:</label>
    <input name = "user" placeholder="請輸入接收信息的用戶"/>
    <label>信息:</label>
    <input name = "msg" placeholder="請輸入信息"/>
    <input type="submit" id="id_submit" name="nm_submit" value="發送" onclick="changeNum()"/>
</form>
<p>已發送<label id = "num">0</label>次消息</p>
<iframe id="id_iframe" name="nm_iframe" style="display:none;"></iframe>
</body>

<script>
    var num = 0;
    function changeNum() {
        num++;
        document.getElementById("num").innerText = num;
    }
</script>
</html>

 

以上,Java搭建的簡易的WebSocket就完成了。

 

總結

好不容易搞出來一個可用的WebSocket的Java代碼,趕緊總結了這篇文章。

本菜鳥也只是剛接觸WebSocket,還是有很多不太明白的地方。(比如服務器代碼部分應該這樣寫嗎?不清楚實際應用場景......)

如果大家按照上述代碼還是無法搭建可用的WebSocket,請聯繫本人;也可以下載本人github上的完整代碼(springboot版,本人親測,絕對可用,大概)。

github源碼下載地址:

https://github.com/BlackHoleSeventh/webSocket

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章