1. 程式人生 > >WebSocket下的聊天室

WebSocket下的聊天室

       websocket是解決客戶端與伺服器端實時通訊而產生的技術。

       websocket協議允許在客戶端和服務端之間建立一條雙向傳遞資訊的通道,它是建立在TCP協議之上的,首先通過”握手“來確認和建立通道,之後客戶端和服務端可以通過這個通道傳遞資訊,而不需要再次發起請求,而且客戶端和服務端都可以主動的傳送訊息。這種技術不依賴於HTTP連線(比如XMLHttpRequest,iframe),可以實現實時訊息傳遞。

與Socket區別:

       Socket其實並不是一個協議,而是為了方便使用TCP或UDP而抽象出來的一層,是位於應用層和傳輸控制層之間的一組介面。Socket是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。當兩臺主機通訊時,必須通過Socket連線,Socket則利用TCP/IP協議建立TCP連線。TCP連線則更依靠於底層的IP協議,IP協議的連線則依賴於鏈路層等更低層次。

       WebSocket則是一個典型的應用層協議。

       Socket是傳輸控制層協議,WebSocket是應用層協議。

WebSocket 客戶端 API :

//建立websocket物件

var ws = new WebSocket(“ws://echo.websocket.org”);

//建立連線

ws.onopen = function(){ws.send(“Test!”); };

//收到伺服器訊息,提取

ws.onmessage = function(evt){console.log(evt.data);ws.close();};

//關閉連線

ws.onclose = function(evt){console.log(“WebSocketClosed!”);};

//產生異常

ws.onerror = function(evt){console.log(“WebSocketError!”);};

  WebSocket 服務端 API :

我們將使用到websocket-api,這個JAR包則在Tomcat的lib下面。

//WebSocket 服務端執行在 ws://[Server 端 IP 或域名]:[Server 埠]/websockets 的訪問端點,客戶端瀏覽器已經可以對 WebSocket 客戶端 API 發起 HTTP 長連線了。
@ServerEndpoint("/websocket")
 public class EchoEndpoint {

//一個新的連線建立時被呼叫
//Session 表明兩個 WebSocket 端點對話連線的另一端,可以理解為類似 HTTPSession 的概念
 @OnOpen
 public void onOpen(Session session) throws IOException {
 //以下程式碼省略...
 }
 
//收到訊息觸發事件
//用於接收傳入的 WebSocket 資訊,這個資訊可以是文字格式,也可以是二進位制格式。
 @OnMessage
 public String onMessage(String message) {
 //以下程式碼省略...
 }

//MaxMessageSize 屬性可以被用來定義訊息位元組最大限制,如果超過 6 個位元組的資訊被接收,就報告錯誤和連線關閉。
 @Message(maxMessageSize=6)
 public void receiveMessage(String s) {
 //以下程式碼省略...
 } 

//傳輸訊息錯誤觸發事件
 @OnError
 public void onError(Throwable t) {
 //以下程式碼省略...
 }
 
//OnClose 在連線被終止時呼叫。引數 closeReason 可封裝更多細節,如為什麼一個 WebSocket 連線關閉。
 @OnClose
 public void onClose(Session session, CloseReason reason) {
 //以下程式碼省略...
 } 
 
 }

聊天室:

伺服器端:

package com.research.WebSocketChatRoom;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;

import com.google.gson.Gson;
//用於建立和配置服務端點
@ServerEndpoint("/websocket")
public class Socket {
    public static Map<String, Session> sessionMap = new HashMap<String, Session>();

    //與某個客戶端的連線會話,需要通過它來給客戶端傳送資料
    private Session session;

    // 一個新的連線建立時被呼叫
    @OnOpen
    public void startSocket(Session session) {
        this.session = session;
        System.out.println("連結成功");
        if (sessionMap.size() == 0) {
            return;
        }
        Set userIds = sessionMap.keySet();
        StringBuffer sBuffer = new StringBuffer();
        for (Object str : userIds) {
            sBuffer.append(str.toString() + ":");
        }
        Gson gson = new Gson();
        try {
            Message message = new Message();
            message.setFrom("系統");
            message.setMsg(sBuffer.toString());
            session.getBasicRemote().sendText(gson.toJson(message), true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 收到訊息觸發事件
    @OnMessage
    public void getMessgae(Session session, String str, boolean last) {
        if (session.isOpen()) {
            try {
                System.out.println(str);
                Gson gson = new Gson();
                Message msg = gson.fromJson(str, Message.class);
                Message toMessage = msg;
                toMessage.setFrom(msg.getId());
                toMessage.setTo(msg.getTo());

                if (msg.getMsg().equals("newUser")) {
                    if (sessionMap.containsKey(msg.getId())) {
                        sessionMap.remove(msg.getId());
                    }
                    sessionMap.put(msg.getId(), session);
                } else {
                    Session toSession = sessionMap.get(msg.getTo());
                    if (toSession != null && toSession.isOpen()) {
                        toSession.getBasicRemote().sendText(gson.toJson(toMessage).toString(), last);
                    } else {
                        toMessage.setMsg("使用者不存在");
                        toMessage.setFrom("系統");
                        session.getBasicRemote().sendText(gson.toJson(toMessage).toString(), last);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        } else {
            System.out.println("session is closed");
        }
    }

    // 關閉連線觸發事件
    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println(session.getId() + "關閉連線");
    }

    // 傳輸訊息錯誤觸發事件
    @OnError
    public void onError(Throwable error) {
        System.out.println(error);
    }
}

 

客戶端:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta charset="UTF-8">
    <title>Test WebSocket</title>
    <script type="text/javascript" src="/resource/js/jquery-3.2.1.min.js"></script>
    <script type="text/javascript">
        $(function () {
            //伺服器地址
            var url = "ws://localhost:8080/websocket";
            var ws = "";
            var message = {"id": "", "msg": "", "form": "", "to": ""};

            function connection() {
                //建立WebSocket物件
                ws = new WebSocket(url);
                console.log("connection.......");
                //收到伺服器訊息,e.data提取
                ws.onmessage = function (e) {
                    var json = eval('(' + e.data.toString() + ')');
                    showMessage(json.from + ":" + json.msg);
                }
                //已經關閉連線
                ws.onclose = function () {
                    showMessage("close");
                }
                //產生異常
                ws.onerror = function (e) {
                    showMessage("error");
                }
                //已經建立連線
                ws.onopen = function () {
                    showMessage("連結成功")
                    message.id = $(".identity").val();
                    message.msg = "newUser";
                    console.log(JSON.stringify(message));
                    //向伺服器傳送訊息
                    ws.send(JSON.stringify(message));
                    message.msg = "";
                }
                //監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。
                window.onbeforeunload = function(){
                    ws.close();
                }

            }


            $(".start-conn-btn").click(function () {
                connection();
            });
            $(".send-btn").click(function () {//send message
                message.to = $(".to-user").val();
                message.msg = $(".msg-context").val();
                $(".msg-context").val("");
                ws.send(JSON.stringify(message));
                showMessage("我:" + message.msg);
                message.msg = "";

            });

            function showMessage(msg) {
                $(".show-message").append(msg + "<br/>");

            }


        });

    </script>
</head>
<body>
<div class="container">
    <div calss="item">
        <span>ID:</span>
        <input type="text" class="identity">
        <button class="start-conn-btn">連結</button>
        <span>toUser:</span>
        <input type="text" class="to-user">
    </div>
    <div class="show-message">

    </div>
    <div calss="item">
        <span>內容:</span>
        <textarea class="msg-context"></textarea>
    </div>
    <div>
        <button class="send-btn">send</button>
    </div>
</div>
</body>

</html>

 

啟動:

/*
 * 文 件 名:  ChanatRoom.java
 * 版    權:  
 * 描    述:  <描述>
 * 修 改 人:   sun
 * 修改時間:  2018年7月18日
 */
package com.home.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * <一句話功能簡述>
 * <功能詳細描述>
 * 
 * @author   sun
 * @version  [版本號, 2018年7月18日]
 */
@Controller
@RequestMapping("/chanat-room")
public class ChanatRoom
{
    @RequestMapping("/start")
    public String start()
    {
        return "/chatRoom/chat";
    }
    
}