websocket實現多房間多用戶聊天室


眾所周知,Web 應用的交互過程通常是客戶端通過瀏覽器發出一個請求,服務器端接收請求后進行處理並返回結果給客戶端,客端瀏覽器將信息呈現。但是對於實時性要求較高、海量並發的應用,比如金融證券的實時信息,web導航應用中地理位置獲取,社交網絡的實時消息推送等。

 

方案一:輪詢,客戶端用js代碼每隔一定時間向服務器發送請求,這樣會造成資源浪費(浪費帶寬),在高並發的情況下還可能造成服務器奔潰。

方案二:基於Flash、AdobeFlash,通過socket實現數據信息交互,再利用Flash暴露的接口供js調用,但是Flash在移動互聯網上的支持不好,IOS和Android都不支持Flash了。

方案三:WebSocket,2014年開始,各大應用服務器和瀏覽器廠商逐步統一,J2EE7也實現了WebSocket協議,無論客戶端還是服務器都提供了對其的支持。


WebSocket介紹與原理

WebSocket 是 HTML5 一種新的協議。它實現了瀏覽器與服務器全雙工通信,能更好的節省服務器資源和帶寬並達到實時通訊,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸數據,但是它和HTTP 最大不同是:

WebSocket 是一種雙向通信協議,在基於http建立連接后,WebSocket 服務器和 browser都能主動向對方發送或接收數據,就像 Socket 一樣;WebSocket 需要類似 TCP 的客戶端和服務器端通過握手連接,連接成功后才能相互通信,實現長連接。

 

WebSocket 客戶端連接報文

GET /webfin/websocket/ HTTP/1.1

Host: localhost

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==

Origin: http://localhost:8080

Sec-WebSocket-Version: 13

 

可以看到,客戶端發起的 WebSocket 連接報文類似傳統 HTTP 報文,”Upgrade:websocket”參數值表明這是 WebSocket 類型請求,“Sec-WebSocket-Key”是 WebSocket 客戶端發送的一個 base64 編碼的密文,要求服務端必須返回一個對應加密的“Sec-WebSocket-Accept”應答,否則客戶端會拋出“Error during WebSocket handshake”錯誤,並關閉連接。

WebSocket 服務端響應報文

HTTP/1.1 101 Switching Protocols

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

 

“Sec-WebSocket-Accept”的值是服務端采用與客戶端一致的密鑰計算出來后返回客戶端的,“HTTP/1.1 101 Switching Protocols”表示服務端接受 WebSocket 協議的客戶端連接,經過這樣的請求-響應處理后,客戶端服務端的 WebSocket 連接握手成功, 后續就可以進行 TCP 通訊了。 --------------------- 作者:zhengholien 來源:CSDN 原文:https://blog.csdn.net/zhengholien/article/details/76696509?utm_source=copy 版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

項目實例

采用spring maven項目,開發工具eclise ,tomcat 版本8.5以上(不需要引用其它jar包)

主要jar包要求:(tomcat8.5 以上不需要引包)

代碼實例

websocket核心配置類

package controller;

 
import java.util.Set;
 
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
import javax.websocket.server.ServerEndpointConfig;
 
/**
 * websockket 核心配置類,項目啟動時會自動啟動,類似與ContextListener.
 */
public class WebSocketConfig implements ServerApplicationConfig{
 
    
    /**
     * 注解方式
     * 掃描src下所有類@ServerEndPoint注解的類。
     */
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> arg0) {
        System.out.println("============="+arg0.size());
        //返回
        return arg0;
    }
 
    /**
     * 獲取所有以接口方式配置的webSocket類。
     */
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> arg0) {
        return null;
    }
 
}

webSocket服務器

 

package controller;
 


import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
 
/**
 * writer: holien
 * Time: 2017-08-01 13:00
 * Intent: webSocket服務器
 */
@ServerEndpoint("/webSocket/chat/{roomName}/{username}")
public class WebSocketServer {
 
    // 使用map來收集session,key為roomName,value為同一個房間的用戶集合
    // concurrentMap的key不存在時報錯,不是返回null
    private static final Map<String, Set<Session>> rooms = new ConcurrentHashMap();
    private static final Map<String, String> userNameList = new ConcurrentHashMap();
    @OnOpen
    public void connect(@PathParam("roomName") String roomName,@PathParam("username") String username, Session session) throws Exception {
        // 將session按照房間名來存儲,將各個房間的用戶隔離
        if (!rooms.containsKey(roomName)) {
            // 創建房間不存在時,創建房間
            Set<Session> room = new HashSet<Session>();
            // 添加用戶
            room.add(session);
            
            rooms.put(roomName, room);
        } else {
            // 房間已存在,直接添加用戶到相應的房間
            rooms.get(roomName).add(session);
        }
        System.err.println("username"+username);
        System.out.println("a client has connected!");
    }
 
    @OnClose
    public void disConnect(@PathParam("roomName") String roomName,@PathParam("userName") String userName, Session session) {
        rooms.get(roomName).remove(session);
        System.out.println("a client has disconnected!");
    }
 
    @OnMessage
    public void receiveMsg(@PathParam("roomName") String roomName,@PathParam("username") String username,
                           String msg, Session session) throws Exception {
        // 此處應該有html過濾
        msg = username + ":" + msg;
        System.out.println(msg);
        // 接收到信息后進行廣播
        broadcast(roomName, msg);
    }
 
    // 按照房間名進行廣播
    public static void broadcast(String roomName, String msg) throws Exception {
        for (Session session : rooms.get(roomName)) {
                session.getBasicRemote().sendText(msg);
        }
    }
 
}

頁面顯示

<!DOCTYPE html><html lang="en">
<head>
    <meta charset="UTF-8">
    <title>網絡聊天室</title>
</head>
<style type="text/css">
    .msg_board {
        width: 322px;
        height: 100px;
        border: solid 1px darkcyan;
        padding: 5px;
        overflow-y: scroll;
        // 文字長度大於div寬度時換行顯示
        word-break: break-all;
    }
    /*set srcoll start*/
    ::-webkit-scrollbar
    {
        width: 10px;
        height: 10px;
        background-color: #D6F2FD;
    }
    ::-webkit-scrollbar-track
    {
        -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
        /*border-radius: 5px;*/
        background-color: #D6F2FD;
    }
    ::-webkit-scrollbar-thumb
    {
        height: 20px;
        /*border-radius: 10px;*/
        -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3);
        background-color: #89D7F7;
    }
    /*set srcoll end*/
</style>
<body>
<label>房間名</label>
<input id="input_roomName" size="10" maxlength="10">
<label>用戶名</label>
<input id="username" size="10" maxlength="10">
<input type="button"  value="進入聊天室" onclick="initWebSocket()" />
<input type="button" value="退出聊天室" onclick="closeWs()" /><br>
<div class="msg_board"></div>
<input id="input_msg" size="43" maxlength="40">
<input type="button" value="發送" onclick="send_msg()" />
</body>
<script type="text/javascript">
    var webSocket;
 
    function send_msg() {
        if (webSocket != null) {
            var input_msg = document.getElementById("input_msg").value.trim();
            if (input_msg == "") {
                return;
            }
            webSocket.send(input_msg);
            // 清除input框里的信息
            document.getElementById("input_msg").value = "";
        } else {
            alert("您已掉線,請重新進入聊天室...");
        }
    };
 
    function closeWs() {
        webSocket.close();
    };
 
    function initWebSocket() {
        var roomName = document.getElementById("input_roomName").value;
        // 房間名不能為空
        if (roomName == null || roomName == "") {
            alert("請輸入房間名");
            return;
        }
        var username = document.getElementById("username").value.trim();
        if (username == "" || username==null) {
            alert("用戶名不能為空")
            return;
        }
        if ("WebSocket" in window) {
//            alert("您的瀏覽器支持 WebSocket!");
            if (webSocket == null) {
                var url = "ws://localhost:8080/webSocketDemo/webSocket/chat/" + roomName+"/"+username;
                // 打開一個 web socket
                webSocket = new WebSocket(url);
            } else {
                alert("您已進入聊天室...");
            }
 
            webSocket.onopen = function () {
                alert("已進入聊天室,暢聊吧...");
            };
 
            webSocket.onmessage = function (evt) {
                var msg_board = document.getElementsByClassName("msg_board")[0];
                var received_msg = evt.data;
                var old_msg = msg_board.innerHTML;
                msg_board.innerHTML = old_msg + received_msg + "<br>";
                // 讓滾動塊往下移動
                msg_board.scrollTop = msg_board.scrollTop + 40;
            };
 
            webSocket.onclose = function () {
                // 關閉 websocket,清空信息板
                alert("連接已關閉...");
                webSocket = null;
                document.getElementsByClassName("msg_board")[0].innerHTML = "";
            };
        }
        else {
            // 瀏覽器不支持 WebSocket
            alert("您的瀏覽器不支持 WebSocket!");
        }
    }
</script>
</html>

只需要這三個文件實現多房間多用戶聊天,

 


免責聲明!

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



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