Netty

目录

简介

特性

NIO简单介绍

入门

DiscardDemo(只有接受没有响应的Server)

模拟telnet通信的客户端和服务器

基于上面的telnet案例来做个简易的群聊系统


简介

Netty是 一个异步事件驱动的网络应用程序框架,一个基于NIO客户端服务器框架,支持多种协议(自定义的传输协议),可以灵活快速的搭建与开发,支持自定义扩展时间模型和通用的通信API并且Byte Buffer实现了零拷贝;用于快速开发可维护的高性能协议服务器和客户端。学netty之前可以先了解一下NIO模型。

特性

  • 使用各种传输类型的统一API--阻塞和非阻塞嵌套字。
  • 基于灵活且扩展的事件模型,可将关注点明确分离。
  • 高度可定制的线程模型-单线程,一个或多个线程池。
  • 从3.1版本后真正的无连接数据报套接字支持。
  • 更高的吞吐量,更低的延迟
  • 减少资源消耗
  • 减少不必要的内存复制
  • 完整的SSL/TLS和StartTLS支持

NIO简单介绍

nio是种异步事件驱动模型,该模型种有3个重要部分:Buffer、Channel、Selector。Nio是基于Channel和Buffer进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中,Selector用于监听多个通道的事件(例如连接、读写操作),因此单个线程可以监听多个数据通道。说白了,NIO网络之间的通信与交互主要用到Channel和Buffer。NIO详细介绍后面会在IO模型中详细介绍。

事件模型图:

 

 

入门

下面我想以例子的入门学习netty。

DiscardDemo(只有接受没有响应的Server)

DiscardServer案例:

public class DiscardServer {

    private int port;

    public DiscardServer(int port){
        this.port = port;
    }

    public static void main(String[] args) {
        new DiscardServer(8080).start();
    }

    private void start() {
        //主处理事件,主要是用于通信连接作用
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //工作处理事件,主要是用于通信的读写等操作
        EventLoopGroup workGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new DiscardHandler());//这里是通过pipeline来将你的处理器追加到channel中。
                        }
                    }).option(ChannelOption.SO_BACKLOG,2)
                    .childOption(ChannelOption.SO_KEEPALIVE,true);
            //异步启动服务。
            ChannelFuture sync = bootstrap.bind(port).sync();
            //异步关闭channel。
            sync.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //优雅停服
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }

}

DiscardHandler:

public class DiscardHandler extends ChannelInboundHandlerAdapter {

    /**
     * 读取客户端发来的消息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            System.out.println("收到客户端的信息。");
            ByteBuf byteBuf = (ByteBuf) msg;
            while (byteBuf.isReadable()) {
                System.out.println((char) byteBuf.readByte());
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    //出现异常的时候会触发
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

模拟telnet通信的客户端和服务器

telnetServer

public class TelnetServer {
    private int port ;

    public TelnetServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        new TelnetServer(8080).start();
    }

    private void start() {

        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    //这里使用了字符串的编解码处理Handler,是因为TelnetServerHandler继承了SimpleChannelInboundHandler,
                                    // 而SimpleChannelInboundHandler,netty已经帮你处理了字符串的编译和解码操作,所以需要加上
                                    .addLast(new StringDecoder())
                                    .addLast(new StringEncoder())
                                    .addLast(new TelnetServerHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }


    }
}
TelnetServerHandler
public class TelnetServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println("收到客户端发来的数据:" + s);
        //把接收到的字符串加个?返回回去
        channelHandlerContext.writeAndFlush(s+"?");
        //还有种写法,客户端和服务端通信主要是通过channel来进行交互,这种写法主要是为了下面群聊系统的伏笔
//        channelHandlerContext.channel().writeAndFlush(s+"?");
    }
}
TelnetClient
public class TelnetClient {
    private static final String IP = "127.0.0.1";
    private static final int port = 8080;

    public static void main(String[] args) {
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(worker)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new StringDecoder())
                                    .addLast(new StringEncoder())
                                    .addLast(new TelnetClientHandler());
                        }
                    });
            Channel channel = bootstrap.connect(IP, port).sync().channel();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            ChannelFuture write = null;
            while (true) {
                String line   = reader.readLine();
                if (line == null ){
                    break;
                }
                if ("bye".equals(line)) {
                    channel.closeFuture().sync();
                    break;
                }
                write = channel.writeAndFlush(line);

                if (write != null ){
                    write.sync();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            worker.shutdownGracefully();
        }
    }
}
TelnetClientHandler
public class TelnetClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println("收到服务端发来的信息:"+s);
    }
}

基于上面的telnet案例来做个简易的群聊系统

ChatServer

public class ChatServer {
    private int port;

    public ChatServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        new ChatServer(8080).start();

    }

    private void start() {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss,worker)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel
                                    .pipeline()
                                    //这里主要是处理粘包的问题,字符串必须是以\r\n结尾才能被认为是一个完整的数据信息。
                                    .addLast(new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()))
                                    .addLast(new StringDecoder())
                                    .addLast(new StringEncoder())
                                    //myHandler
                                    .addLast(new MyServerHandler());
                        }
                    })
                    .childOption(ChannelOption.SO_KEEPALIVE,true);
            bootstrap.bind(port).sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
MyServerHandler
public class MyServerHandler extends SimpleChannelInboundHandler<String> {
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        SocketAddress socketAddress = channel.remoteAddress();
        String welcome = new Date() +"欢迎"+socketAddress+"来到聊天室\r\n";
        channel.writeAndFlush(welcome);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channels.add(channel);
        for (Channel c : channels) {
            if (c == channel) {
                continue;
            }
            c.writeAndFlush(c.remoteAddress()+"上线"+"\r\n");
        }
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        channels.remove(channel);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        Channel channel = ctx.channel();
        SocketAddress socketAddress = channel.remoteAddress();
        System.out.println(socketAddress+"说:"+s);
//        channel.writeAndFlush(socketAddress+"说:"+s);
//        ctx.writeAndFlush("aaaa");
        for (Channel c : channels) {
            if (c == channel) {
                continue;
            }
            c.writeAndFlush(socketAddress+"说:"+s+"\r\n");
        }
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Channel channel = ctx.channel();
        SocketAddress socketAddress = channel.remoteAddress();
        String leave = socketAddress+"离开了聊天室"+"\r\n";
        for (Channel c : channels) {
            if (c == channel) {
                continue;
            }
            c.writeAndFlush(leave);
        }
    }
}
ChatClient
public class ChatClient {
    private static final int PORT = 8080;
    private static final String IP = "127.0.0.1";

    public static void main(String[] args) {

        EventLoopGroup worker = new NioEventLoopGroup(1);

        try {
            Bootstrap b = new Bootstrap();
            b.group(worker)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()))
                                    .addLast(new StringDecoder())
                                    .addLast(new StringEncoder())
                                    //myHandler
                                    .addLast(new MyClientHandler());
                        }
                    });
            Channel channel = b.connect(IP, PORT).sync().channel();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            ChannelFuture writeAndFlush = null;
            while (true) {
                try {
                    String line = reader.readLine();

                    if (line == null) {
                        break;
                    }

                    if ("bye".equals(line)) {
                        channel.closeFuture().sync();
                        break;
                    }

                    writeAndFlush = channel.writeAndFlush(line + "\r\n");
                    if (writeAndFlush != null) {
                        writeAndFlush.sync();
                    }
                } catch (IOException e) {
                    System.out.println("写数据异常。。。"+e.getMessage());
                }
            }

        } catch (InterruptedException e) {
            System.out.println("异常。。。"+e.getMessage());
        } finally {
            worker.shutdownGracefully();
        }
    }

}
MyClientHandler
public class MyClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
        System.out.println(s);
    }
}

上面的例子都是通过netty中example项目中的案例进行演变和研究,后面会持续更新

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值