基于netty的简单聊天室

一、Netty 简介

Netty 是基于 Java NIO 的异步事件驱动的网络应用框架,使用 Netty 可以快速开发网络应用,Netty 提供了高层次的抽象来简化 TCP 和 UDP 服务器的编程,但是你仍然可以使用底层的 API。

Netty 的内部实现是很复杂的,但是 Netty 提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的。

Netty 是最流行的 NIO 框架,它已经得到成百上千的商业、商用项目验证,许多框架和开源组件的底层 rpc 都是使用的 Netty,如 Dubbo、Elasticsearch 等等。下面是官网给出的一些 Netty 的特性:

设计方面

  • 对各种传输协议提供统一的 API(使用阻塞和非阻塞套接字时候使用的是同一个 API,只是需要设置的参数不一样)。
  • 基于一个灵活、可扩展的事件模型来实现关注点清晰分离。
  • 高度可定制的线程模型——单线程、一个或多个线程池。
  • 真正的无数据报套接字(UDP)的支持(since 3.1)。

易用性

  • 完善的 Javadoc 文档和示例代码。
  • 不需要额外的依赖,JDK 5 (Netty 3.x) 或者 JDK 6 (Netty 4.x) 已经足够。

性能

  • 更好的吞吐量,更低的等待延迟。
  • 更少的资源消耗。
  • 最小化不必要的内存拷贝。

安全性

  • 完整的 SSL/TLS 和 StartTLS 支持

二、代码实现

导入依赖

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

netty服务器类

public class NettyServer {
    public static void main( String[] args ) {
        //创建两个线程池
        NioEventLoopGroup mainGrp = new NioEventLoopGroup();
        NioEventLoopGroup subGrp = new NioEventLoopGroup();

        try {
            //创建Netty服务器启动对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //初始化服务器启动对象
            serverBootstrap
                    //指定使用上面创建的两个线程池
                    .group(mainGrp,subGrp)
                    //指定channel通道类型
                    .channel(NioServerSocketChannel.class)
                    //指定通道初始化器用来加载当Channel收到时间消息后,
                    .childHandler(new WebSocketChannelInitializer());
            //绑定服务器端口,以同步的方式启动服务器
            ChannelFuture future = serverBootstrap.bind(9090).sync();
            if(future.isSuccess()){
                System.out.println("服务启动成功!");
            }
            //等待服务器关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //关闭服务器
            mainGrp.shutdownGracefully();
            subGrp.shutdownGracefully();
        }
    }
}

通道初始化

public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 初始化通道
     * 在这个方法中去加载对应的ChannelHandler
     * @param socketChannel
     * @throws Exception
     */
    @Override
    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());
    }
}

ChatHandler

public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    //用来保存所有的客户端连接
    private static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private SimpleDateFormat sdf = new SimpleDateFormat();

    //当Channel中有新的时间消息会自动调用
    @Override
    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
        clients.add(ctx.channel());
    }
}

前端页面

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

    接收到的消息:
    <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("当前浏览器不支持websocket");
        }

        function sendMsg(){
            var message = document.getElementById("message")
            websocket.send(message.value);
        }

    </script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值