Websocket簡單實現多人聊天室


WebSocket簡介
    WebSocket是一種在單個TCP連接上進行全雙工通信的協議。WebSocket通信協議於2011年被IETF定為標准RFC 6455,並由RFC7936補充規范。WebSocket API也被W3C定為標准。

    WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。

Websocket優點
  • 較少的控制開銷。在連接創建后,服務器和客戶端之間交換數據時,用於協議控制的數據包頭部相對較小。在不包含擴展的情況下,對於服務器到客戶端的內容,此頭部大小只有2至10 字節(和數據包長度有關);對於客戶端到服務器的內容,此頭部還需要加上額外的4字節的 掩碼。相對於HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減少了。
  • 更強的實時性。由於協議是全雙工的,所以服務器可以隨時主動給客戶端下發數據。相對於HTTP請求需要等待客戶端發起請求服務端才能響應,延遲明顯更少;即使是和Comet等類似的長輪詢比較,其也能在短時間內更多次地傳遞數據。
  • 保持連接狀態。與HTTP不同的是,Websocket需要先創建連接,這就使得其成為一種有狀態的協議,之后通信時可以省略部分狀態信息。而HTTP請求可能需要在每個請求都攜帶狀態信息(如身份認證等)。
  • 更好的二進制支持。Websocket定義了 二進制幀,相對HTTP,可以更輕松地處理二進制內容。
  • 可以支持擴展。Websocket定義了擴展,用戶可以擴展協議、實現部分自定義的子協議。如部分瀏覽器支持 壓縮等。
  • 更好的壓縮效果。相對於 HTTP壓縮,Websocket在適當的擴展支持下,可以沿用之前內容的 上下文,在傳遞類似的數據時,可以顯著地提高壓縮率。

下面實現我們的多人聊天室
  簡單看一下我們的效果
    
    

 

 

   這里就是我們一個簡單的效果

再來看一下具體是怎樣實現的
  
1、首先創建我們的maven項目
   2、導入我們的pom依賴
      下面是我pom依賴的全部代碼

    

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.ybf</groupId>
  <artifactId>liaotianshi2</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <!-- Spring Boot 啟動父依賴 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>
    <properties>
        <!-- 項目設置 : 編碼格式 UTF-8 及 springboot 相關版本 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
        <druid>1.0.18</druid>
    </properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        
        <!-- Spring Boot Test 依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
  <build>
    <finalName>liaotianshi</finalName>
  </build>
</project>


  3、創建我們的SpringBootWebSocketHandler類

package com.ybf.test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;

@Component
public class SpringBootWebSocketHandler implements WebSocketHandler {

    // 存儲所有客戶端的會話 WebSocketSession,key 使用客戶端的唯一標識方便存取

//    private static Map<String, WebSocketSession> allWebSocketSession = new HashMap<String, WebSocketSession>();
    private static List<WebSocketSession> sessionList = new ArrayList<>();
    /**
     * 與服務器成功建立連接
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("客戶端成功建立連接=>" + session.getId());
        sessionList.add(session);
    }

    /**
     * 接受客戶端的消息
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        // 獲取客戶端的消息
        String msg = message.getPayload().toString();
//        System.out.println("接受到的客戶端消息:" + msg);
        // 將數據存入會話 回傳給客戶端
//        session.sendMessage(new TextMessage("服務器接收到的消息:" + msg));
        sendMsg(msg);
    }

    //給所有客戶端發信息的方法
    private void sendMsg(String msg) throws IOException {
        for (WebSocketSession session : sessionList) {
            if(session.isOpen()) {
                session.sendMessage(new TextMessage(msg));
            }
        }
    }
    
    /**
     * 通訊異常時
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("通訊出現異常");
    }

    /**
     * 當連接關閉時
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        // TODO Auto-generated method stub
        System.out.println("連接已關閉");
    }

    /**
     * 是否允許分段發送
     */
    @Override
    public boolean supportsPartialMessages() {
        // 一次性發送
        return false;
    }

}

 

  4、再來創建我們的SpringBootWebSocketConfigurer 類

package com.ybf.test;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration // 單例模式 bean
@EnableWebSocket // 啟動 WebSocket 服務器
public class SpringBootWebSocketConfigurer implements WebMvcConfigurer, WebSocketConfigurer {
    @Autowired
    private SpringBootWebSocketHandler handler;

//    @Autowired
//    private SpringBootHandshakeInterceptor handshakeInterceptor;
    
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // -------------------- 允許跨域訪問 WebSocket ------------------------
        String[] allowsOrigins = { "*" };// 允許連接的域 , 只能以 http 或 https 開頭
       
        // 7. 設置 websocket 服務器地址 ws://localhost:8080/SpringBootWebSocket
        registry.addHandler(handler, "/SpringBootWebSocket").setAllowedOrigins(allowsOrigins);
    }
    
    /**
     * 這下面的代碼是沒有什么用的 
     * 主要是第一個方法
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Validator getValidator() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public MessageCodesResolver getMessageCodesResolver() {
        // TODO Auto-generated method stub
        return null;
    }
}

 

  5、創建我們的前台頁面

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聊天室</title>
<link rel="stylesheet" href="layui/css/layui.css" media="all">
<script src="http://apps.bdimg.com/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
    $(document).ready(
            function() {
                var urlPrefix = "ws://localhost:8080/SpringBootWebSocket";
                var ws = null;
                // 加入聊天室  
                $('#bt_join').click(function() {
                    if(ws != null){
                        alert("用戶【"+$('#username').val()+"】已加入連接");
                        return;
                    }
                    //建立連接
                    
                     //判斷當前瀏覽器是否支持WebSocket
                      if ('WebSocket' in window) {
                          ws = new WebSocket(urlPrefix);
                      }
                      else {
                        alert('當前瀏覽器 Not support websocket');
                      }
                    
                    ws.onopen = function(event) {
                        console.log("與服務器建立連接");
                        ws.send("您的好友【"+$('#username').val()+"】上線了");
                    };
                    
                    // 接收服務端返回給前端的消息 
                    ws.onmessage = function(event) {
                        $('#text_chat_content').append(
                                event.data + "\n");
                    };
                    ws.onclose = function() {
                        console.log("與服務器斷開連接");
                        $('#text_chat_content').append(
                                "用戶【" + $('#username').val() + "】離開聊天室!" + "\n");
                        $("#username").val("");
                    };
                });
                // 發送消息  
                $('#bt_send').click(function() {
                    if (ws == null) {
                        alert("該用戶不在線");
                        return;
                    }
                    var msg = $('#in_msg').val();
                    ws.send("用戶【" + $('#username').val() + "】:" + msg);
                });
                // 離開聊天室  
                $('#bt_left').click(function() {
                    ws.send("用戶【" + $('#username').val() + "】離開聊天室!");
                    $('#in_msg').val("");
                    ws.close();
                });
            })
</script>
</head>
<body>
    聊天消息內容:
    <br />
    <textarea id="text_chat_content" readonly="readonly" class="layui-textarea" style="height: 400px; width: 600px;"></textarea>
    <br /> 輸入框:
    <br />
    <div class="layui-input-inline">
        <textarea id="in_msg" placeholder="請輸入內容" class="layui-textarea" style="height: 100px; width: 500px;">
        </textarea>
    </div>
    <button type="button" id="bt_send" class="layui-btn layui-btn-radius">發送信息</button>
    <br />&nbsp;<br />
    <label class="layui-form-mid">用戶:</label>
    <div class="layui-input-inline">
        <input type="text" id="username" placeholder="請輸入姓名" class="layui-input">
    </div>
    <button type="button" id="bt_join" class="layui-btn layui-btn-radius layui-btn-normal">加入聊天室</button>
    <button type="button" id="bt_left" class="layui-btn layui-btn-radius layui-btn-normal">離開聊天室</button>
</body>
</html>

 

注意:
  我這里是加的 layui 效果,當然也可以不加layui效果,這就看自己的意思了
    這是 layui 官網地址 https://www.layui.com/ 大家有興趣可以了解一下

下面就是我的項目框架

 

 

 

 

 

 

 

 

 

 


免責聲明!

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



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