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/ 大家有兴趣可以了解一下

下面就是我的项目框架

 

猜你喜欢

转载自www.cnblogs.com/study-yu/p/12502059.html