Netty学习之读netty权威指南(二)

写了上一篇觉得写的不太好,今天开始看Netty了,好好学,好好写。

Netty入门

Netty的环境搭建呢咱们就不细说了大家百度搜一下,挺简单的。书中的入门是写了一个Netty的例子,那咱们也写这个例子看看,Netty到底是怎么玩的。写例子之前回忆一下NIO的时候,写一个时间服务器的步骤

  1. 创建一个 ServerSocketChannel,设置为非阻塞的。
  2. 绑定监听端口,设置最大的连接数。
  3. 创建单独的线程用来轮询 Selector。
  4. 创建Selector,将之前创建的 ServerSocketChannel 注册进来,并关心对应的事件。
  5. 启动线程,执行 selector.select() 方法,轮询就绪的Channel
  6. 当轮询到就绪的Channel之后,判断其所关注的事件。
  7. 如果是新加入的连接,将其设置为非阻塞式,设置其他的TCP参数,并注册到Selector上监听OP_READ
  8. 如果是读事件,证明SocketChannel中有新的数据,创建ByteBuffer处理
  9. 如果是写事件,证明SocketChannel中有数据没写完,继续处理

大家可以看到,一个简单的NIO服务端,我们直接使用JDK的类库开发的话需要大概十来步,相当麻烦。下面我们看看Netty开发的服务端代码。

TimeServer

package NettyServerBasic;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class TimeServer {
    public void bind(int port) throws Exception {
        //两个线程组,各包含一组NIO线程
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            //ServerBootstrap是Netty的启动NIO的辅助类,目的降低服务端开发的难度
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
                    //书上说这个对应的是JDK的NIO类库的NioServerSocketChannel
                    //我个人认为应该netty给咱创建一个NioServerSocketChannel类似的channel
                    .channel(NioServerSocketChannel.class)
                    //配置NioServerSocketChannel的TCP参数  1024是最大连接数
                    //跟serverSocketChannel的bind方法类似吧
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //绑定一个处理请求的类  我这里用一个匿名内部类写了
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    });
            //绑定端口     ChannelFuture异步回调
            ChannelFuture future = bootstrap.bind(port).sync();
            //等待服务器监听端口关闭
            future.channel().close().sync();
        }finally {
            //关系线程池
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }


    public static void main(String[] args) throws Exception {
        int port = 8989;
        new TimeServer().bind(port);
    }
}
TimeServerHandler
package NettyServerBasic;


import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;

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

public class TimeServerHandler extends ChannelInboundHandlerAdapter {


    /*** 服务端读取到网络数据后的处理*/
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);

        String body = new String(bytes,"UTF-8");
        String currentTime = "获取当前时间".equals(body)?new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()):"指令有误";
        ByteBuf buf = Unpooled.copiedBuffer(currentTime.getBytes());

        //将消息写到缓冲数组中
        ctx.write(buf);

    }


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        //将消息推到SocketChannel中发送给客户端
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

大家注意这有一个坑,TimeServerHander书上是继承自 ChannelHandlerAdapter,当我继承完之后发现没有channelRead、channelReadComplete、exceptionCaught三个方法,其实Netty4和Netty5的版本不一样了,我这用的是Netty4书上用的是Netty5,大家练习的时候一定要注意,不然找好久都找不到问题所在。下面我在网上找的,大家看一下。

Netty4的ChannelHandlerAdapter:

Netty5的ChannelHandlerAdapter:

但是在Netty4中用的话只能用ChannelHandlerAdapter的子类,我这就是用的他的子类ChannelInboundHandlerAdapter ,下图所示:

服务器端开发到这里就结束了,具体里面的每个组件什么意思咱们后边慢慢补上,先黑盒的看代码就好。下面开始客户端的代码了。

TimeClent

package NettyServerBasic;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class TimeClient {

    public void connect(int port, String host)  {
        //配置客户端的NIO线程
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
        //服务器端是ServerBootstrap
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY,true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel){
                        socketChannel.pipeline().addLast(new TimeClientHandler());
                    }
                });
        //发起异步连接


            ChannelFuture  channelFuture = bootstrap.connect(host,port).sync();
            //等待客户端的连接关闭
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            //释放IO线程组
            eventLoopGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new TimeClient().connect(8989,"127.0.0.1");
    }
}

TimeClientHandler:

package NettyServerBasic;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class TimeClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    private final ByteBuf sendMsg;

    public TimeClientHandler() {
        byte[] bytes = "获取当前时间".getBytes();
        sendMsg = Unpooled.buffer(bytes.length);
        sendMsg.writeBytes(bytes);
    }


    //读取到服务器端的数据后的操作
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        String body = new String(bytes,"UTF-8");
        System.out.println("当前时间是:"+body);


    }
    /*客户端被通知channel活跃以后,做事*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //往服务器写数据
        ctx.writeAndFlush(sendMsg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

好了以上就是Netty客户端和服务端的开发基本的东西,大家先黑盒的了解一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值