Netty简易聊天室

这篇具有很好参考价值的文章主要介绍了Netty简易聊天室。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文目的

  • 通过一个简易的聊天室案例,讲述Netty的基本使用。同时分享案例代码。
  • 项目中用到了log4j2,junit5,同时分享这些基础组件的使用。
  • 项目中用到了awt,属于古董技术,只是用来做界面。非重点不用关注。

环境说明

开发工具:idea2023,jdk:1.8,Maven:3.6.3

maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.xxx</groupId>
    <artifactId>xxx</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>xxx</name>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.21</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

        <!-- log4j2-slf4j-适配器 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.20.0</version>
        </dependency>

        <!-- log4j2 日志核心 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.20.0</version>
        </dependency>

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.96.Final</version>
        </dependency>

        <!-- 单元测试,Junit5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.3</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

日志配置

src/main/resources/log4j2.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!-- log4j2配置文件 -->
<!-- monitorInterval="30" 自动加载配置文件的间隔时间,不低于10秒;生产环境中修改配置文件,是热更新,无需重启应用
 status="info" 日志框架本身的输出日志级别,可以修改为info, -->
<Configuration status="warn" monitorInterval="30">
    <!-- 集中配置属性,使用时通过:${LOG_HOME} -->
    <properties>
        <!-- 当前项目名称,供下方引用 -->
        <property name="PROJECT_NAME" value="tank-battle"/>
        <!-- 默认日志格式-包名自动缩减(同步异步通用) -->
        <property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%-5t|%logger{1.}: %msg%n"/>
        <!-- 日志格式-打印代码的精确位置信息,类,方法,行。(建议同步使用)。异步如果打印位置信息,会有严重性能问题 -->
        <property name="LOG_PATTERN_ALL" value="%d{yyyy-MM-dd HH:mm:ss.SSS}|%-5level|%-5t|%location: %msg%n"/>
        <!-- 日志主目录。如果想把日志输出到tomcat底下时使用。 -->
        <property name="LOG_HOME">${web:rootDir}/WEB-INF/logs</property>
    </properties>

    <!-- 日志打印输出方式 -->
    <Appenders>
        <Console name="STDOUT" target="SYSTEM_OUT">
            <PatternLayout charset="UTF-8" Pattern="${LOG_PATTERN}"/>
        </Console>

        <RollingFile name="FileLog" fileName="logs/${PROJECT_NAME}.log" filePattern="logs/${PROJECT_NAME}-%d_%i.log">
            <PatternLayout charset="UTF-8" Pattern="${LOG_PATTERN}"/>
            <Policies>
                <!-- 每天生成一个,同时如果超过10MB还会再生成 -->
                <TimeBasedTriggeringPolicy/>
                <SizeBasedTriggeringPolicy size="50 MB"/>
            </Policies>
            <DefaultRolloverStrategy max="99"/>
        </RollingFile>
    </Appenders>

    <!-- 将代码路径与上面的日志打印关联起来 -->
    <Loggers>
        <!-- 当前项目日志 -->
        <Logger name="com.sjj" level="INFO" additivity="false">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FileLog"/>
        </Logger>

        <!-- 第三方依赖项目日志 -->
        <logger name="org.springframework" level="info"/>
        <logger name="org.jboss.netty" level="warn"/>

        <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
        <!-- 根节点日志,除了上面配置的之外的日志 -->
        <Root level="WARN">
            <AppenderRef ref="STDOUT"/>
            <AppenderRef ref="FileLog"/>
        </Root>
    </Loggers>
</Configuration>

单元测试

确认项目已加入Junit5依赖,就是如下这段。

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.3</version>
            <scope>test</scope>
        </dependency>

新建单元测试类的步骤。

  1. 在要创建单元测试的功能类上,依次点Code > generate > Test
  2. 然后在弹出的窗口中,选择Junit版本为5,测试类名,测试方法等。然后点确定。
  3. Netty简易聊天室,Java,Netty,java
  4. IDEA会自动根据功能类的路径在test目录中创建相同路径但以Test结尾的测试类。并且会自动生成勾选方法的默认测试代码。
  5. 根据程序的输入和输出,编写单元测试代码。
  6. 点击方法左边的绿色三角形就可以执行单元测试用例了。

为什么要进行单元测试?

  1. 方法内部可以很复杂,如果靠肉眼观察,比较耗时间。单元测试可以根据入参和返回值测试方法是否达到要求。
  2. 代码是开发人员写的,最了解代码逻辑的还是开发人员。测试人员测试不到代码细节。
  3. 在一个大的功能中,可能会有很多方法,每个方法都要写Main方法来一个个测试比较复杂,而且也不知道测了哪些场景。

为什么有的公司不做单元测试。

  1. 代码业务可能比较简单,程序员读代码不是很费力。
  2. 写单元测试需要额外花时间,程序员工作比较忙,没时间写。

功能介绍

简易版聊天室程序。主要用于练习Netty的使用。聊天室功能如下:

  1. 聊天室支持多客户端,每个客户端都可以看到其他客户端的消息。
  2. 点击关闭按钮时,关闭当前客户端,同时在服务端的客户端列表中也删除。
  3. 系统UI非重点,一切从简。

开发步骤

  1. 首先写一个聊天室的界面(ChatFrame.java)

    1. 参考坦克大战的界面部分,设置好聊天室的长宽和坐标。

    2. 界面包含2个输入部分,中间文本域显示当前聊天室的所有聊天内容。底部文本框输入当前用户的聊天内容

    3. 聊天室窗口初始化时,需要与服务端建立连接。

    4. 当用户输入完聊天内容后回车,需要将聊天内容通过Netty客户端发送给服务端。

    5. 当用户关闭窗口时,关闭当前客户端,同时在服务端的客户端列表中也删除。

    6. /**
       * 聊天室客户端-界面<br>
       *
       * @author namelessmyth
       * @version 1.0
       * @date 2023/8/15
       */
      @Slf4j
      public class ChatFrame extends Frame {
          public static final int GAME_WIDTH = ConfigUtil.getInt("chat.frame.width");
          public static final int GAME_HEIGHT = ConfigUtil.getInt("chat.frame.height");
          TextArea ta = new TextArea();
          TextField tf = new TextField();
      
          public static final ChatFrame INSTANCE = new ChatFrame();
      
          public static void main(String[] args) throws Exception {
              INSTANCE.setVisible(true);
              ChatClient.connect();
          }
      
          private ChatFrame() throws HeadlessException {
              //创建游戏的主Frame
              this.setTitle("chat room");
              this.setSize(GAME_WIDTH, GAME_HEIGHT);
              this.setLocation(800, 100);
              this.add(ta, BorderLayout.CENTER);
              this.add(tf, BorderLayout.SOUTH);
      
              tf.addActionListener(new ActionListener() {
                  @Override
                  public void actionPerformed(ActionEvent e) {
                      ChatClient.send(tf.getText());
                      tf.setText("");
                  }
              });
      
              this.addWindowListener(new WindowAdapter() {
                  @Override
                  public void windowClosing(WindowEvent e) {
                      ChatClient.close();
                      System.exit(0);
                  }
              });
              log.info("chat room Main frame initialization completed");
          }
      
          public void updateText(String text) {
              ta.setText(ta.getText() + Constants.LINE_SEPERATOR + text);
          }
      }
      
  2. 编写Netty客户端与服务端进行消息通信(ChatClient.java)。

    1. 参考上面的描述,客户端需要实现如下方法。

      1. connect(),与服务端建立连接的方法
      2. send(),向服务端发送聊天消息的方法。
      3. channelRead,读取服务端信息更新客户端聊天内容方法
      4. 参考代码如下
    2. @Slf4j
      public class ChatClient {
          private static SocketChannel channel;
      
          /**
           * 与服务端建立连接的方法
           */
          public static void connect() {
              EventLoopGroup group = new NioEventLoopGroup(1);
              try {
                  Bootstrap b = new Bootstrap();
                  b.group(group);
                  b.channel(NioSocketChannel.class);
                  b.handler(new ChannelInitializer<SocketChannel>() {
                      @Override
                      protected void initChannel(SocketChannel ch) throws Exception {
                          channel = ch;
                          ch.pipeline().addLast(new MyClientHandler());
                      }
                  });
                  ChannelFuture cf = b.connect("localhost", 8888).sync();
                  //直到服务器被关闭,否则一直阻塞。
                  cf.channel().closeFuture().sync();
                  log.info("the chat client has been closed.");
              } catch (Exception e) {
                  log.error("ChatClient.connect.Exception.", e);
              } finally {
                  group.shutdownGracefully();
              }
          }
      
          /**
           * 向服务端发送聊天消息的方法
           * @param msg 聊天内容
           */
          public static void send(String msg) {
              channel.writeAndFlush(Unpooled.copiedBuffer(msg.getBytes()));
              log.info("client.send().{}", msg);
          }
      
          /**
           * 关闭客户端方法,向服务端发送特定消息告知其删除本客户端。
           */
          public static void close() {
              send("__88__");
              channel.close();
          }
      }
      
      @Slf4j
      class MyClientHandler extends ChannelInboundHandlerAdapter {
          /**
           * 读取服务端数据
           * @param msg 服务端数据
           */
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
              ByteBuf buf = (ByteBuf) msg;
              String text = buf.toString(StandardCharsets.UTF_8);
              ChatFrame.INSTANCE.updateText(text);
              log.info("channelRead.msg:{}", text);
          }
      
          /**
           * 连接刚建立时的事件处理
           */
          @Override
          public void channelActive(ChannelHandlerContext ctx) throws Exception {
              log.info("connected to server.");
          }
      
          /**
           * 异常处理
           */
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
              log.error("chat client exceptionCaught:", cause);
              super.exceptionCaught(ctx, cause);
          }
      }
      
  3. 聊天室服务端(ChatServer.java)。

    1. 服务端需要记录所有的客户端。(可能有多个)

    2. 当某个客户端发来消息之后,需要将消息转发给所有客户端。

    3. 当接收到特殊消息时(客户端关闭),需要将客户端从列表中移除。

    4. @Slf4j
      public class ChatServer {
          static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
      
          public static void main(String[] args) throws Exception {
              //总管线程组
              EventLoopGroup bossGroup = new NioEventLoopGroup(1);
              //接待员线程
              EventLoopGroup workerGroup = new NioEventLoopGroup(2);
              //服务器启动辅助类
              ServerBootstrap b = new ServerBootstrap();
              //放在第一位的是总管线程组,第二位的就是接待员线程组。
              b.group(bossGroup, workerGroup);
              //异步全双工
              b.channel(NioServerSocketChannel.class);
              //接收到客户端连接的处理,相当于BIO的accept
              b.childHandler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  protected void initChannel(SocketChannel sc) throws Exception {
                      log.info("a client connected:{}", sc);
                      sc.pipeline().addLast(new MyChildHandler());
                  }
              });
              b.bind(8888).sync();
          }
      }
      
      @Slf4j
      class MyChildHandler extends ChannelInboundHandlerAdapter {
          @Override
          public void channelActive(ChannelHandlerContext ctx) throws Exception {
              ChatServer.clients.add(ctx.channel());
          }
      
          /**
           * 读取客户端通道内的数据
           * @param msg 客户端消息
           * @throws Exception
           */
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
              ByteBuf buf = (ByteBuf) msg;
              String str = buf.toString(StandardCharsets.UTF_8);
              log.info("channelRead().input,string:{},buf:{}", str, buf);
              if (StrUtil.equalsIgnoreCase(str, "__88__")) {
                  ChatServer.clients.remove(ctx.channel());
                  ctx.close();
                  log.info("The chat client has been closed:{}", ctx.channel());
              } else {
                  ChatServer.clients.writeAndFlush(msg);
                  log.info("ChatServer.clients.writeAndFlush:{}", msg);
              }
          }
      
          /**
           * 异常处理
           *
           * @param ctx
           * @param cause
           * @throws Exception
           */
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
              log.error("exceptionCaught:", cause);
              ChatServer.clients.remove(ctx.channel());
              ctx.close();
          }
      }
      
    5. 补充服务端关闭的处理(仅思路,未实现)。

      1. 通知客户端,服务器准备关闭。
      2. 拒绝新的连接接入
      3. 等待所有客户端都处理完成。
      4. 开始关闭流程,发送消息给客户端,客户端自动处理。
      5. 确认所有客户端断开。
      6. server保存现有的工作数据。
      7. 停止线程组
      8. 退出。
    6. 服务端UI

      1. 为了可以方便的看到所有客户端的连接情况和消息,以及后续进一步实现服务端的关闭效果考虑在服务端实现UI

      2. 新增一个ServerFrame类,实现服务端UI,服务端左边显示消息,右边显示客户端的连接情况。

      3. ServerFrame类初始化时自动启动服务端。服务端接收消息时打印到消息窗口中。

      4. 有客户端连上或者关闭时显示到右边的窗口中。

      5. 实现效果如下图

      6. Netty简易聊天室,Java,Netty,java

      7. 参考代码如下。(只需要修改服务端代码,客户端不变)

      8. @Slf4j
        public class ServerFrame extends Frame {
            public static final int GAME_WIDTH = ConfigUtil.getInt("server.frame.width");
            public static final int GAME_HEIGHT = ConfigUtil.getInt("server.frame.height");
            TextArea tmsg = new TextArea("messages:");
            TextArea tclient = new TextArea("clients:");
        
            public static final ServerFrame INSTANCE = new ServerFrame();
        
            public static void main(String[] args) throws Exception {
                INSTANCE.setVisible(true);
                ChatServer.start();
            }
        
            private ServerFrame() throws HeadlessException {
                //创建游戏的主Frame
                this.setTitle("chat room");
                this.setSize(GAME_WIDTH, GAME_HEIGHT);
                this.setLocation(100, 100);
        
                tmsg.setFont(new Font("Calibri",Font.PLAIN,20));
                tclient.setFont(new Font("Calibri",Font.PLAIN,20));
        
                Panel p = new Panel(new GridLayout(1, 2));
                p.add(tmsg);
                p.add(tclient);
                this.add(p);
        
                this.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }
                });
                log.info("Server Main frame initialization completed");
            }
        
            public void updateMsg(String text) {
                tmsg.setText(tmsg.getText() + Constants.LINE_SEPERATOR + text);
            }
        
            public void updateClient(String text) {
                tclient.setText(tclient.getText() + Constants.LINE_SEPERATOR + text);
            }
        }
        
        @Slf4j
        public class ChatServer {
            static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        
            public static void start(){
                //总管线程组
                EventLoopGroup bossGroup = new NioEventLoopGroup(1);
                //接待员线程
                EventLoopGroup workerGroup = new NioEventLoopGroup(2);
                try {
                    //服务器启动辅助类
                    ServerBootstrap b = new ServerBootstrap();
                    //放在第一位的是总管线程组,第二位的就是接待员线程组。
                    b.group(bossGroup, workerGroup);
                    //异步全双工
                    b.channel(NioServerSocketChannel.class);
                    //接收到客户端连接的处理,相当于BIO的accept
                    b.childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel sc) throws Exception {
                            log.info("a client connected:{}", sc);
                            sc.pipeline().addLast(new MyChildHandler());
                        }
                    });
                    log.info("chat server has been started");
                    ChannelFuture cf = b.bind(8888).sync();
                    cf.channel().closeFuture().sync();
                } catch (Exception e) {
                    log.error("ChatServer.exception", e);
                } finally {
                    bossGroup.shutdownGracefully();
                    workerGroup.shutdownGracefully();
                    log.info("chat server has been closed");
                }
            }
        }
        
        @Slf4j
        class MyChildHandler extends ChannelInboundHandlerAdapter {
            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                ServerFrame.INSTANCE.updateClient("client connected:"+ctx.channel().remoteAddress());
                ChatServer.clients.add(ctx.channel());
            }
        
            /**
             * 读取客户端通道内的数据
             *
             * @param msg 客户端消息
             * @throws Exception
             */
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                ByteBuf buf = (ByteBuf) msg;
                String str = buf.toString(StandardCharsets.UTF_8);
                log.info("channelRead().input,string:{},buf:{}", str, buf);
                if (StrUtil.equalsIgnoreCase(str, "__88__")) {
                    ChatServer.clients.remove(ctx.channel());
                    ctx.close();
                    ServerFrame.INSTANCE.updateClient("client closed>"+ctx.channel().remoteAddress());
                    log.info("The chat client has been closed:{}", ctx.channel());
                } else {
                    ChatServer.clients.writeAndFlush(msg);
                    ServerFrame.INSTANCE.updateMsg(ctx.channel().remoteAddress() + ">" + str);
                    log.info("ChatServer.clients.writeAndFlush:{}", msg);
                }
            }
        
            /**
             * 异常处理
             *
             * @param ctx
             * @param cause
             * @throws Exception
             */
            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                log.error("ChatServer.exceptionCaught:", cause);
                ChatServer.clients.remove(ctx.channel());
                ctx.close();
            }
        }
        
      9. 启动顺序。先启动ServerFrame,然后启动ChatFrame,ChatFrame可以启动多个。

      10. 多个客户端发送消息都会在服务端显示。

参考说明

本文内容主要来源于马士兵老师的视频教程(Java经典实战项目-坦克大战),结合了老师的讲课内容以及自己的实践做了一些补充。文章来源地址https://www.toymoban.com/news/detail-679666.html

到了这里,关于Netty简易聊天室的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【 基于Netty实现聊天室聊天业务学习】第4节.什么是BIO与NIO

    IO在读写的时候是阻塞的,无法做其他操作,并发处理能力的非常低,线程之间访问资源通信时候也是非常耗时久,依赖我们的网速,带宽。 我们看一下他的白话原理 我们来看一下这张图那么这张图的话它里面有一个server还有三个客户端那么客户端的话它可以有很多,那么我

    2024年04月26日
    浏览(48)
  • LinuxC TCP实现简易聊天室

    目录 1.概述 1.1聊天室设计内容 2.系统设计 2.1系统功能设计 2.1.1用户管理 2.1.2聊天室管理 2.1.3聊天管理 2.2系统数据结构设计 2.3系统主要函数设计 3.系统实现 3.2功能模块的程序流程图及运行界面 3.2.1功能模块流程图  3.2.2运行界面 4.源代码 4.1客户端 4.2服务器 注:存在问题 1

    2024年02月09日
    浏览(39)
  • 基于netty+springCloudGateWay+nacos 实现的聊天室(可支持集群)

    1.基于需要实现一个IM 就记录一下 。 1.将websocket 注册到 nacos 服务上 也是IM 注册到nacos 上  借助rabbitmq 实现集群以及redis 实现记录离线消息具体看代码文字懒得写(里面自己还尝试换了 zookeeper 作为注册中心 仅供学习)IM 和 网关的 目录: 整个逻辑是启动服务将websocket,网关

    2023年04月08日
    浏览(75)
  • 【Java socket编程】多人聊天室

    课程设计大作业 功能: 1.具有 点对点通信 功能,任意客户端之间能够发送消息。 2.具有 群组通信 功能: 客户端可以 自主建立群组 ,添加或删除组成员; 客户端能够向组内成员同时发送消息,其他组成员不能收到。 3.具有 广播 功能 客户端能够向所有其他成员 广播消息

    2024年02月04日
    浏览(37)
  • Java+Vue实现聊天室(WebSocket进阶-聊天记录)

    WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服

    2024年02月11日
    浏览(58)
  • Spring boot 项目(二十三)——用 Netty+Websocket实现聊天室

    Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API。 Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑。

    2023年04月15日
    浏览(54)
  • websocket+elementui+vue实现简易聊天室

    搭建服务端环境 安装socket.io 服务端基于node,js的express框架生成,所以写成模块,引入至app.js中 其中,io.sockets.emit用于向所有建立连接的客户端发送信息,socket.broadcast.emit用于向除发送方之外的客户端发送信息。 客户端基于vue和elementui 进入聊天页面后,先判断用户是否登录,

    2024年04月25日
    浏览(46)
  • 基于springboot与websocket实现简易聊天室

    创建一个简单聊天室的HTML和JavaScript代码,匹配相应的css样式,这个聊天室将使用WebSocket技术,允许用户实时发送和接收消息。 1.1 html和js代码 通过new WebSocket(url)建立一个新的websocket连接。websocket连接建立之后使用websocket.onopen,websocket.onclose,websocket.onerror等方法实时监测we

    2024年02月01日
    浏览(52)
  • 基于java的聊天室系统设计与实现

    基于java的聊天室系统设计与实现 研究背景: 随着互联网技术的迅速发展和普及,在线聊天和实时通信成为人们日常交流的重要方式。聊天室系统作为实时通信的一种重要形式,具有广泛的应用场景,如远程教育、在线游戏、即时通讯、网络直播等。因此,设计和实现一个基

    2024年02月05日
    浏览(41)
  • 基于WebSocket的简易聊天室的基本实现梳理

    目前在很多网站为了实现推送技术所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信

    2024年02月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包