Netty实现WebSocket通信

/*
* Netty Reactor模型 -
* 1.单线程模型:一个用户一个线程来处理,线程有极限
* 2.多线程模型:加入线程池,线程池线程轮询执行任务
* 3.主从多线程模型:俩个线程池,一个线程池接收请求,一个线程池处理IO(推荐,适用高并发环境)
*
* 以下代码为主从多线程模型
* */

映入坐标:
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
</dependencies>


服务器端:
package cn.web.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/*
* Netty Reactor模型 -
* 1.单线程模型:一个用户一个线程来处理,线程有极限
* 2.多线程模型:加入线程池,线程池线程轮询执行任务
* 3.主从多线程模型:俩个线程池,一个线程池接收请求,一个线程池处理IO(推荐,适用高并发环境)
*
* 以下代码为主从多线程模型
* */
public class WebSocketServer {
    public static void main(String[] args) {
        NioEventLoopGroup mainGrp=new NioEventLoopGroup();//主线程池
        NioEventLoopGroup subGrp=new NioEventLoopGroup();//从线程池

        try {
            //1.创建netty服务器启动对象
            ServerBootstrap serverBootstrap=new ServerBootstrap();

            //2.初始化
            serverBootstrap
                    //指定使用的线程池
                    .group(mainGrp,subGrp)
                    //指定netty通道类型
                    .channel(NioServerSocketChannel.class)
                    // 指定通道初始化器用来加载当Channel收到事件消息后,
                    // 如何进行业务处理
                    .childHandler(new WebSocketChannelInitializer());

            //3.绑定端口,以同步的方式启动
            ChannelFuture future=serverBootstrap.bind(9090).sync();

            //4.等待服务关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();

            //5.异常关闭服务器
            mainGrp.shutdownGracefully();
            subGrp.shutdownGracefully();
        }

    }
}

package cn.web.netty;

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;
import io.netty.handler.timeout.IdleStateHandler;


/**
* 通道初始化器
* 用来加载通道处理器(ChannelHandler)
*/
public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {

// 初始化通道
// 在这个方法中去加载对应的ChannelHandler
protected void initChannel(SocketChannel socketChannel) throws Exception {

// 获取管道,将一个一个的ChannelHandler添加到管道中
ChannelPipeline pipeline = socketChannel.pipeline();

// 添加一个http的编解码器
pipeline.addLast(new HttpServerCodec());
// 添加一个用于支持大数据流的支持
pipeline.addLast(new ChunkedWriteHandler());
// 添加一个聚合器,这个聚合器主要是将HttpMessage聚合成FullHttpRequest/Response
pipeline.addLast(new HttpObjectAggregator(1024*64));
// 需要指定接收请求的路由
// 必须使用以ws后缀结尾的url才能访问
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
// 添加自定义的Handler
pipeline.addLast(new ChatHandler());

// 增加心跳事件支持
// 第一个参数: 读空闲4秒
// 第二个参数: 写空闲8秒
// 第三个参数: 读写空闲12秒
pipeline.addLast(new IdleStateHandler(4,8,12));
pipeline.addLast(new HearBeatHandler());
}
}
 
package cn.web.netty;

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;

import java.text.SimpleDateFormat;
import java.util.Date;


public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    //存储用户连接
    private static ChannelGroup clients=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-mm-dd hh:MM:ss");

    // 当Channel中有新的事件消息会自动调用
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        String text=textWebSocketFrame.text();
        System.out.println("接收到消息:"+text);

        // 获取客户端发送过来的文本消息
        for (Channel client :clients) {
            // 将消息发送到所有的客户端
            client.writeAndFlush(new TextWebSocketFrame(sdf.format(new Date())+":"+text));
        }
    }

    // 当有新的客户端连接服务器之后,会自动调用这个方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        clients.add(ctx.channel());
    }

    // 端口连接处理
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("断开连接");
        clients.remove(ctx.channel());
    }
}

 

package cn.web.netty;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class HearBeatHandler extends ChannelInboundHandlerAdapter {
    // 客户端在一定的时间没有动作就会触发这个事件
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent){
            IdleStateEvent event=(IdleStateEvent)evt;

            if (event.state()== IdleState.READER_IDLE){
                System.out.println("读空闲");
            }else if(event.state()==IdleState.WRITER_IDLE){
                System.out.println("写空闲");
            }else if(event.state()==IdleState.ALL_IDLE){
                System.out.println("读写都空闲你,关闭通道");
                ctx.channel().close();
            }
        }
    }
}

 

HTML页面连接:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>在线聊天室</title>
</head>
<body>
    <input type="text" id="message">
    <input type="button" value="发送消息" onclick="sendMsg()">

    接收到的消息:
    <p id="server_message" style="background-color: #AAAAAA"></p>

    <script>

        var websocket = null;

        // 判断当前浏览器是否支持websocket
        if(window.WebSocket) {
            websocket = new WebSocket("ws://127.0.0.1:9090/ws");

            websocket.onopen = function() {
                console.log("建立连接.");
            }
            websocket.onclose = function() {
                console.log("断开连接");
            }
            websocket.onmessage = function(e) {
                console.log("接收到服务器消息:" + e.data);
                var server_message = document.getElementById("server_message");
                server_message.innerHTML += e.data + "<br/>";
            }
        }
        else {
            alert("当前浏览器不支持web socket");
        }

        function sendMsg() {
            var message = document.getElementById("message");
            websocket.send(message.value);
        }
    </script>
</body>
</html>

 

posted @ 2020-11-03 15:28  zhuyapeng  阅读(4200)  评论(1编辑  收藏  举报