1. 程式人生 > >分享基於 websocket 網頁端聊天室

分享基於 websocket 網頁端聊天室

ebs eight ger true 連接 pointer float min hat

博客地址:https://ainyi.com/#/67

有一個月沒有寫博客了,也是因為年前需求多、回家過春節的原因,現在返回北京的第二天,想想,應該也要分享技術專題的博客了!!

主題

基於 websocket 網頁端聊天室

WebSocket 協議是基於 TCP 的一種新的網絡協議。它實現了瀏覽器與服務器全雙工 (full-duplex) 通信——允許服務器主動發送信息給客戶端。

使用 java 開發後臺

需要導入一個jar包:javax.websocket-api-1.0-rc4.jar

後臺代碼

package com.krry.socket;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
 
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
 
//該註解用來指定一個URI,客戶端可以通過這個URI來連接到WebSocket。類似Servlet的註解mapping。無需在web.xml中配置。
@ServerEndpoint("/websocket")
public class MyWebSocket {
    //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。
    private static int onlineCount = 0;
     
    //concurrent包的線程安全Set,用來存放每個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通信的話,可以使用Map來存放,其中Key可以為用戶標識
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
     
    //與某個客戶端的連接會話,需要通過它來給客戶端發送數據
    private Session session;
     
    /**
     * 連接建立成功調用的方法
     * @param session  可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據
     */
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        webSocketSet.add(this);     //加入set中
        addOnlineCount();           //在線數加1
        System.out.println("有新連接加入!當前在線人數為" + getOnlineCount());
    }
     
    /**
     * 連接關閉調用的方法
     */
    @OnClose
    public void onClose(){
        webSocketSet.remove(this);  //從set中刪除
        subOnlineCount();           //在線數減1    
        System.out.println("有一連接關閉!當前在線人數為" + getOnlineCount());
    }
     
    /**
     * 收到客戶端消息後調用的方法
     * @param message 客戶端發送過來的消息
     * @param session 可選的參數
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("來自客戶端的消息:" + message);
         
        //群發消息
        for(MyWebSocket item: webSocketSet){             
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
                continue;
            }
        }
    }
     
    /**
     * 發生錯誤時調用
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error){
        System.out.println("發生錯誤");
        error.printStackTrace();
    }
     
    /**
     * 這個方法與上面幾個方法不一樣。沒有用註解,是根據自己需要添加的方法。
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException{
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }
 
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
 
    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }
     
    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

前端代碼

註意

前端需要實現這幾個方法:

  // 註冊事件
  // 監聽打開連接
  ws.onopen = function(){
    openWs();
  };
  // 監聽消息
  ws.onmessage = function(event){
    msgWs(event);
  };
  // 監聽關閉連接
  ws.onclose = function(){
    closeWs();
  };
  // 監聽發送錯誤
  ws.onerror = function(){
    errorWs();
  };

具體代碼

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<!doctype html>
<html>
  
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <meta name="keywords" content="">
    <meta name="description" content="">
    <title>
      基於Java服務器端的消息主動推送技術揭秘 --krry
    </title>
    <link rel="stylesheet" href="css/animate.css" />
    <link rel="stylesheet" type="text/css" href="css/sg.css" />
    <style>
      *{margin:0;padding:0;} body{background:url("images/5.jpg");background-size:cover;}
      h1{margin-top:50px;text-align:center;color:#fff;text-shadow:1px 1px 1px
      #000;font-family:-webkit-body;font-size:24px;} .box{width:700px;margin:20px
      auto;} .box span{color:#f60;font-size:16px;font-family:"微軟雅黑";} .box .shu{text-indent:1em;height:24px;font-family:"微軟雅黑";border:0;outline:none;font-size:14px;}
      .box .add{width:300px;margin-right:24px;} .box .user{width:200px;} .box
      .btn{width:80px;height:34px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;margin-top:20px;font-size:16px;font-family:"微軟雅黑";}
      .box .area{line-height: 29px;height:280px;width:680px;padding:10px;overflow:auto;font-size:16px;font-family:"微軟雅黑";margin:20px
      0;outline:none;box-shadow:1px 2px 18px #000} .box .setex{text-indent:1em;height:28px;border:1px
      solid #6c0;width:618px;outline:none;float:left;font-family:"微軟雅黑";} .box
      .send{font-size:14px;width:80px;height:30px;color:#fff;background:#6c0;border:0;outline:none;cursor:pointer;font-family:"微軟雅黑";}
    </style>
  </head>
  
  <body>
    <h1>
      基於Java服務器端的消息主動推送技術揭秘 --krry
    </h1>
    <div class="box">
      <span>
        服務器地址:
      </span>
      <input type="text" class="shu add" value="www.ainyi.com/krry_NetChat/websocket"
      readonly/>
      <span>
        用戶名:
      </span>
      <input type="text" class="shu user" value="匿名" />
      <input type="button" value="連接" class="btn" />
      <div class="area" id="boxx">
      </div>
      <div class="c_cen">
        <input type="text" class="setex" />
        <input type="button" value="發送" class="send">
      </div>
    </div>
    <script src="js/jquery-1.11.1.min.js">
    </script>
    <script src="js/sg.js">
    </script>
    <script src="js/sgutil.js">
    </script>
    <script>
      var close = true;
      var ws;
      $(function() {
        $(".c_cen").hide();
        //首先判斷瀏覽器是否支持webSocket,支持h5的瀏覽器才會支持
        if (window.WebSocket) {
          printMsg("您的瀏覽器支持WebSocket,您可以嘗試連接到聊天服務器!", "OK");
        } else {
          printMsg("您的瀏覽器不支持WebSocket,請選擇其他瀏覽器!", "ERROR");
          //設置按鈕不可點擊
          $(".btn").attr("disabled", "true");
        }
      });
      //打印信息
      function printMsg(msg, msgType) {
        if (msgType == "OK") {
          msg = "<span style=‘color:green‘>" + msg + "</span>";
        }
        if (msgType == "ERROR") {
          msg = "<span style=‘color:red‘>" + msg + "</span>";
        }
        $(".area").append(msg + "<br/>");
        var boxx = document.getElementById("boxx");
        boxx.scrollTop = boxx.scrollHeight; //使滾動條一直在底部
      }

      //打開Socket
      function openWs() {
        printMsg("鏈接已建立", "OK");
        ws.send("【" + $(".user").val() + "】已進入聊天室");
        $(".c_cen").show();
      }

      //接收消息的時候
      function msgWs(e) {
        printMsg(e.data);
      }
      //關閉連接
      function closeWs() {
        $(".btn").val("連接");
        $(".c_cen").hide();
      }
      //產生錯誤
      function errorWs() {
        printMsg("您與服務器連接錯誤...", "ERROR");
      }

      //點擊發送按鈕
      $(".send").click(function() {
        var text = $(".setex").val();
        if (text == null || text == "") return;
        $(".setex").val("");
        ws.send("【" + $(".user").val() + "】:" + text);
      });

      //點擊連接
      $(".btn").click(function() {
        if ($(".add").val() && $(".user").val()) {
          if (close) {
            printMsg("正在準備連接服務器,請稍等...");
            var url = "wss://" + $(".add").val();
            if ("WebSocket" in window) {
              ws = new WebSocket(url);
            } else if ("MozWebSocket" in window) {
              ws = new MozWebSocket(url);
            }
            //已連接
            $(".btn").val("斷開");
            close = false;

            //註冊事件
            ws.onopen = function() {
              openWs();
            };
            ws.onmessage = function(event) {
              msgWs(event);
            };
            ws.onclose = function() {
              closeWs();
            };
            ws.onerror = function() {
              errorWs();
            };

            //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
            window.onbeforeunload = function() {
              ws.send("【" + $(".user").val() + "】離開了聊天室");
              close = true;
              ws.close();
            };

          } else {
            ws.send("【" + $(".user").val() + "】離開了聊天室");
            close = true;
            ws.close();
          }
        } else {
          $.tmDialog.alert({
            open: "left",
            content: "服務器地址和用戶名不能為空哦...",
            title: "提示哦~~~"
          });
        }
      });

      //回車鍵
      $(".setex").keypress(function(event) {
        if (event.keyCode == 13) {
          $(".send").trigger("click");
        }
      });
    </script>
  </body>

</html>

到這裏大功告成

聊天方法

  1. 打開兩個窗口輸入項目地址進行聊天
  2. 可以把鏈接發給朋友打開,進行聊天

來一波截圖

技術分享圖片

技術分享圖片

移動端

技術分享圖片

技術分享圖片

在線演示

PC 端:https://www.ainyi.com/krry_NetChat
移動端:https://www.ainyi.com/krry_NetChatPho

打完收工~

博客地址:https://ainyi.com/#/67

分享基於 websocket 網頁端聊天室