SpringBoot+Netty实现简单通信功能

项目结构
在这里插入图片描述
导入依赖

		<!-- 整合web开发依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 整合netty开发依赖 -->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

application.properties

server.port=8081
spring.resources.static-locations=classpath:/templates/
spring.mvc.view.suffix=.html

ChatHandler

package com.qs.server;

import io.netty.channel.*;
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 qingshi
 * @date 2023/1/18 15:32
 * 处理消息的handler
 */
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);

        //ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器主动推送消息给指定客户端"));//给单个客户端发送消息

        for(Channel channel : clients) {//给所有客户端发送消息
            // 不能直接writeAndFlush收到的content字符串信息,必须封装到frame载体中输出
            channel.writeAndFlush(new TextWebSocketFrame("系统消息:" + content));
        }
         //clients.writeAndFlush(new TextWebSocketFrame("系统消息:" + content));//给所有客户端发送消息,效果同上for循环
    }

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

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

WebSocketServerInitializer

package com.qs.server;

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 qingshi
 * @date 2023/1/18 15:29
 */
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());
    }
}

NettyApplication

package com.qs;

import com.qs.server.WebSocketServerInitializer;
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;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NettyApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(NettyApplication.class, args);

        // 定义一对线程组  主线程组  用于接收客户端的连接请求,不做任何处理
        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();
        }
    }

}

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-Websocket</title>
    <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";
                //alert("服务器发送:"+event.data)
            };
            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>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值