服务端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();
}