服务端NettyServer:

①根据前面说到的Netty架构,对于服务端,我们也需要先新建两个NioEventLoopGroup,分别是boos和worker,boos是负责进行连接请求的接收accept事件,而worker则是负责业务处理


//创建bossGroup和workerGroup,这两个都是无限循环
//只是处理连接请求accept,含有的子线程有多少个呢,NioEventLoop个数,默认是cpu的核数*2
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
//真正的和客户端进行业务处理,含有的子线程有多少个呢,NioEventLoop个数,默认是cpu的核数*2
NioEventLoopGroup workerGroup = new NioEventLoopGroup();


②创建完上面两个线程组之后,则需要创建一个服务端启动对象,配置启动参数


ServerBootstrap serverBootstrap = new ServerBootstrap();


下面使用链式编程来进行配置:

serverBootstrap.group(bossGroup, workerGroup)//设置两个线程组                             .channel(NioServerSocketChannel.class)//使用nioserversocketchannel作为通道实现           .option(ChannelOption.SO_BACKLOG, 128)//设置线程队列等待连接个数           .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持连接状态         .childHandler(new ChannelInitializer<SocketChannel>() {//创建通道测试对象(匿名对象)piepline里面设置处理器

    //这里会进行客户端业务处理
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        //这里加入了自定义的handler
        ChannelPipeline pipeline = ch.pipeline().addLast(new NettyServerHandler());//通过chnnel可以得到pipeline
    }
});//给workergruop的eventgroup设置处理器

※这里比较重要的是最后一步的childHandler,这里的childHandler就是给我们的workerloop实际处理业务逻辑的pipeline配置一个handler,handler可以定制化逻辑。


//绑定一个端口,并且同步,生成一个channelfuture对象,后面会详聊,简单的认为是立马返回的对象,
//这里就是启动服务器,因为绑定了端口
ChannelFuture channelFuture = serverBootstrap.bind(6668).sync();
//对关闭通道进行监听,当有关闭通道的消息的时候,才会进行close
channelFuture.channel().closeFuture().sync();
finally {
  //优雅的关闭
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}


上面就是netty简单的实现了服务端,下面看下自定义的handler是怎样实现的:


//自定义的适配器需要继承netty自己的适配器,这样我们自定义的handler才能称为handler


public class NettyServerHandler extends ChannelInboundHandlerAdapter


Netty自己的handler有几个主要的方法: (1)channelRead,这个方法当有数据读写的时候,会触发,可以读取客户端的消息


channelRead(ChannelHandlerContext ctx, Object msg)


里面有两个参数传入,一个是ChannelHandlerContext--上下文对象,包含了管道pipeline,通道channel,地址remoteAdress等信息

一个是Object,就是客户端发送过来的对象,因为数据是在channel中,所以我们最好使用buffer来进行接收,因此一般我们都需要对msg进行强转


@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

    System.out.println("server ctx:"+ ctx);
    System.out.println("看看channel和pipeline关系");
    Channel channel = ctx.channel();
    ChannelPipeline pipeline = ctx.pipeline();//本质是双向链表,出栈入栈问题

    //因为现在数据在channel,所以我们最好用buffer接收---这里的buffer跟nio的buffer有区别
    //强转
    ByteBuf buf=  (ByteBuf)msg;
    System.out.println("读取到的数据为:"+ buf.toString(CharsetUtil.UTF_8));
 //   System.out.println("客户端地址:"+ctx.channel().remoteAddress());
}


(2)channelReadComplete,数据读取完毕之后,需要做的业务操作,回消息

@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    //把数据write到缓冲区,同时进行flush,就是把缓存数据写到管道
    //一般来讲,我们需要对发送的数据进行编码
    ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端",CharsetUtil.UTF_8));
}

(3)exceptionCaught,发生异常的handler


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


服务端NettyClient:

①客户端跟服务端有些许区别,第一个就是不需要两个loopgroup了,不需要使用boosgroup了,所以只需要新建一个loopgroup来处理业务就行了


NioEventLoopGroup eventgruop = new NioEventLoopGroup();


②第二点就是不是使用serverBootstrap,而是使用bootstrap


//客户端使用的是bootstrap
Bootstrap bootstrap = new Bootstrap();


③设置启动参数

//设置相关参数
bootstrap.group(eventgruop)//设置线程组
         .channel(NioSocketChannel.class)//设置客户端通道的实现类
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             protected void initChannel(SocketChannel ch) throws Exception {
                 ch.pipeline().addLast(new NettyClientHandler());//加入自己的处理器
             }
         });


ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); System.out.println("客户端开始连接"); //给通道关闭进行监听---当发生了通道关闭消息的时候会进行关闭 channelFuture.channel().closeFuture().sync();


finally {
    //优雅的关闭
    eventgruop.shutdownGracefully();
}


下面就是客户端的自定义handler:

(1)channelActive,当通道就绪,就会触发该方法


@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
    System.out.println("client:"+ctx);
    //发送消息
    ctx.writeAndFlush(Unpooled.copiedBuffer("Hello Server,miao", CharsetUtil.UTF_8));
}


(2)channelRead,当通道有读取事件时,会触发


@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

  ByteBuf buf=  (ByteBuf) msg;

  //服务器回复的消息
    System.out.println("客户端收到的消息:"+buf.toString(CharsetUtil.UTF_8));
    System.out.println("服务器的地址:"+ctx.channel().remoteAddress());

}


(3)exceptionCaught,当有异常的就会触发
 


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