SpringBoot+Netty+WebSocket实现简单的在线聊天小功能

效果演示视频和教学讲解视频地址:演示地址
SpringBoot+Netty+WebSocket实现简单的在线聊天小功能_第1张图片

注意:先启动SpringBoot项目,再启动WebSocketServer!!!
代码:
1.pom.xml代码:



<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.0modelVersion>

  <parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.2.5.RELEASEversion>
    <relativePath/> 
  parent>

  <groupId>com.yjq.programmergroupId>
  <artifactId>ChatDemoartifactId>
  <version>1.0-SNAPSHOTversion>

  <name>ChatDemoname>
  
  <url>http://www.example.comurl>

  <properties>
    <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
    <maven.compiler.source>1.8maven.compiler.source>
    <maven.compiler.target>1.8maven.compiler.target>
  properties>

  <dependencies>
    
    <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-webartifactId>
    dependency>
    
    <dependency>
      <groupId>io.nettygroupId>
      <artifactId>netty-allartifactId>
      <version>4.1.42.Finalversion>
    dependency>
  dependencies>

  <build>
    <pluginManagement>
      <plugins>

        <plugin>
          <groupId>org.springframework.bootgroupId>
          <artifactId>spring-boot-maven-pluginartifactId>
        plugin>


      plugins>
    pluginManagement>
  build>
project>

2.WebSocketServer代码:

package com.yjq.programmer.websocket;

import com.yjq.programmer.netty.HelloServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @author 杨杨吖
 * @QQ 823208782
 * @WX yjqi12345678
 * @create 2022-10-03 8:28
 */
public class WebSocketServer {

    public static void main(String[] args) throws Exception {
        // 定义一对线程组  主线程组  用于接收客户端的连接请求,不做任何处理
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 定义一对线程组  从线程组  主线程组会把任务丢给从线程组,让从线程组去处理
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // netty服务器的创建,ServerBootstrap是一个启动类
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup) // 设置主从线程组
                    .channel(NioServerSocketChannel.class) // 设置NIO双向通道类型
                    .childHandler(new WebSocketServerInitializer()); // 子处理器,用于处理workerGroup

            // 启动server,绑定8088端口启动,并且同步等待方式启动
            ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();

            // 监听关闭的channel, 设置为同步的方式
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 关闭我们的主线程组和从线程组
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}

3.WebSocketServerInitializer代码:

package com.yjq.programmer.websocket;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 * @author 杨杨吖
 * @QQ 823208782
 * @WX yjqi12345678
 * @create 2022-10-03 8:34
 */
public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        // 通过SocketChannel去获取对应的管道pipeline
        ChannelPipeline pipeline = channel.pipeline();
        // 通过管道,添加handler  HttpServerCodec是netty自己提供的助手类
        // 当请求到服务端时候,我们需要做解码,响应到客户端时候需要做编码
        pipeline.addLast("HttpServerCodec", new HttpServerCodec());
        // 对写大数据流的支持
        pipeline.addLast("ChunkedWriteHandler", new ChunkedWriteHandler());
        // 对HttpMessage进行聚合 聚合成FullHttpRequest或FullHttpResponse
        // 几乎在netty的编程中,都会用到这个handler
        pipeline.addLast("HttpObjectAggregator", new HttpObjectAggregator(1024*64));

        // 以上用于支持http协议

        // websocket服务器处理的协议  并且用于指定给客户端连接访问的路由:/ws
        pipeline.addLast("WebSocketServerProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
        // 自定义的handler
        pipeline.addLast(new ChatHandler());
    }
}

4.ChatHandler代码:

package com.yjq.programmer.websocket;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * @author 杨杨吖
 * @QQ 823208782
 * @WX yjqi12345678
 * @create 2022-10-03 8:50
 */

/**
 * 处理消息的handler
 * TextWebSocketFrame:在netty中,是用于为websocket专门处理文本的对象,frame是消息的载体
 */
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    // 用于记录和管理所有客户端的ChannelGroup
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        // 获取客户端传输过来的消息
        String content = msg.text();
        System.out.println("接收到的数据:" + content);

        for(Channel channel : clients) {
            // 不能直接writeAndFlush收到的content字符串信息,必须封装到frame载体中输出
            channel.writeAndFlush(new TextWebSocketFrame("系统消息:" + content));
        }
        // clients.writeAndFlush(new TextWebSocketFrame("系统消息:" + content));

    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // 当客户端连接服务端之后,获取客户端的channel,并且放到ChannelGroup中去进行管理
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 这步是多余的,当断开连接时候ChannelGroup会自动移除对应的channel
        clients.remove(ctx.channel());
        System.out.println(ctx.channel().id().asLongText());
    }
}

5.index.html代码:

DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Netty-Websockettitle>
    <script type="text/javascript">
        let socket;
        if(window.WebSocket){
            socket = new WebSocket("ws://localhost:8088/ws");
            socket.onmessage = function(event){
                let textarea = document.getElementById('responseText');
                textarea.value += event.data+"\r\n";
            };
            socket.onopen = function(event){
                let textarea = document.getElementById('responseText');
                textarea.value = "Netty-WebSocket服务器。。。。。。连接  \r\n";
            };
            socket.onclose = function(event){
                let textarea = document.getElementById('responseText');
                textarea.value = "Netty-WebSocket服务器。。。。。。关闭 \r\n";
            };
        } else {
            alert("您的浏览器不支持WebSocket协议!");
        }

        function send(){
            if(!window.WebSocket){return;}
            if(socket.readyState === WebSocket.OPEN) {
                let message = document.getElementById('message').value;
                socket.send(message);
            } else {
                alert("WebSocket 连接没有建立成功!");
            }

        }

    script>
head>
<body>
<form onSubmit="return false;">
    <label>文本label><input type="text" id="message" name="message" placeholder="这里输入消息" /> <br />
    <br /> <input type="button" value="发送ws消息"
                  onClick="send()" />
    <hr color="black" />
    <h3>服务端返回的应答消息h3>
    <textarea id="responseText" style="width: 1024px;height: 300px;">textarea>
form>
body>
html>

6.项目结构及配置文件:
SpringBoot+Netty+WebSocket实现简单的在线聊天小功能_第2张图片

你可能感兴趣的:(开发记录,websocket,spring,boot,java)