Netty+Websocket实现简单的回声聊天服务器

Netty Websocket 回声聊天室案例

讲解参照我netty聊天室的博客
Demo源码地址https://gitee.com/HumorChen/netty_websocket_demo 仅供参考

  • 项目名

    netty_websocket_demo
    
  • Maven依赖

    完整依赖在最后

    <!--netty的依赖集合,都整合在一个依赖里面了-->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.25.Final</version>
    </dependency>
    
  • NettyWebServer类

    package com.example.netty_websocket_demo.server;
    
    import com.example.netty_websocket_demo.handler.NettyWebSocketHandler;
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.*;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    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;
    
    public class NettyWebServer {
        public static void run (){
            //boss线程组用来接收连接
            EventLoopGroup boss=new NioEventLoopGroup();
            //worker线程组用来处理消息
            EventLoopGroup worker=new NioEventLoopGroup();
            try {
                //辅助启动工具类
                ServerBootstrap bootstrap=new ServerBootstrap();
                bootstrap.group(boss,worker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline=ch.pipeline();
                        //http编码解码器
                        pipeline.addLast(new HttpServerCodec());
                        //分块传输
                        pipeline.addLast(new ChunkedWriteHandler());
                        //块聚合
                        pipeline.addLast(new HttpObjectAggregator(1024*1024));
                        //协议处理器
                        pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                        //自定义的处理器
                        pipeline.addLast(new NettyWebSocketHandler());
                    }
                });
                //绑定端口,同步启动
                ChannelFuture future=bootstrap.bind(8081).sync();
                future.channel().closeFuture().sync();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                boss.shutdownGracefully();
                worker.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) {
            run();
        }
    }
    
    
  • NettyWebSocketHandler类

    package com.example.netty_websocket_demo.handler;
    
    import io.netty.channel.*;
    import io.netty.channel.group.ChannelGroup;
    import io.netty.channel.group.DefaultChannelGroup;
    import io.netty.handler.codec.http.websocketx.*;
    import io.netty.util.concurrent.GlobalEventExecutor;
    
    /**
     * NettyWebsocket文本消息处理器
     */
    public class NettyWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
        // 用于记录和管理所有客户端的channle
        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) {
    //			channel.writeAndFlush(
    //				new TextWebSocketFrame(
    //						"[服务器在]" + LocalDateTime.now()
    //						+ "接受到消息, 消息为:" + content));
    //		}
            // 下面这个方法,和上面的for循环,一致   向客户端发送数据
            clients.writeAndFlush(new TextWebSocketFrame("我是服务器,我收到你的消息为:" + content));
        }
    
        /**
         * 当客户端连接服务端之后(打开连接)
         * 获取客户端的channle,并且放到ChannelGroup中去进行管理
         */
        @Override
        public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
            clients.add(ctx.channel());
        }
    
        @Override
        public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            // 当触发handlerRemoved,ChannelGroup会自动移除对应客户端的channel,所以下面的remove不用我们再手写
            //		clients.remove(ctx.channel());
            System.out.println("客户端断开,channle对应的长id为:" + ctx.channel().id().asLongText());
            System.out.println("客户端断开,channle对应的短id为:" + ctx.channel().id().asShortText());
        }
    }
    
    
    
  • 验证使用SpringBoot和一个html页面

  • Application类

    package com.example.netty_websocket_demo;
    
    import com.example.netty_websocket_demo.server.NettyWebServer;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.event.ApplicationReadyEvent;
    import org.springframework.context.ApplicationListener;
    
    @SpringBootApplication
    public class NettyWebsocketDemoApplication implements ApplicationListener<ApplicationReadyEvent> {
    
        public static void main(String[] args) {
            SpringApplication.run(NettyWebsocketDemoApplication.class, args);
        }
    
        @Override
        public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
            //springboot启动完成后启动netty
            NettyWebServer.run();
        }
    }
    
    
  • Html页面

    index.html (src\main\resources\static\index.html)
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>WebSocketTest</title>
        <script>
            url='ws://localhost:8081/ws'
            if (!window.WebSocket) {
                window.WebSocket = window.MozWebSocket
            }
            if (window.WebSocket) {
                socket = new WebSocket(url)
                socket.onopen = function(event) {
                    addMsg('连接成功')
                    console.log(event)
                }
                socket.onclose = function(event) {
                    addMsg('连接被关闭')
                }
                socket.onmessage = function(event) {
                    addMsg("【服务器】:"+event.data)
                }
            } else {
                alert('你的浏览器不支持 WebSocket!')
            }
    
            function addMsg(msg) {
                let content=document.getElementById('content')
                content.innerHTML=content.innerHTML+msg+"<br>"
            }
            function send() {
                let msg=document.getElementById('input').value
                if(msg){
                    if (!window.WebSocket) {
                        return
                    }
                    if (socket.readyState == WebSocket.OPEN) {
                        socket.send(msg)
                        addMsg("【客户端】:"+msg)
                        document.getElementById('input').value=''
                    } else {
                        alert('连接没有开启.')
                    }
                }else{
                    alert('不允许空消息')
                }
            }
        </script>
    </head>
    <body>
    <div  id="content" style="width: 800px;height: 600px;">
    
    </div>
    <div style="width: 800px;height: 120px">
        <textarea  id="input" placeholder="请输入短信内容" style="width: 100%;height: 80px"></textarea>
        <button onclick="send()">发送</button>
    </div>
    </body>
    </html>
    
    - 启动 NettyWebsocketDemoApplication
    - 浏览器访问localhost即可测试
    
posted @ 2020-12-04 11:35  HumorChen99  阅读(2)  评论(0编辑  收藏  举报  来源