Netty学习笔记(一) 客户端与服务端的搭建
客户端
ChannelFuture channelFuture = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new StringEncoder());
}
})
// connect 异步非阻塞线程
.connect(new InetSocketAddress("localhost", 8080));
// 同步阻塞,等待nio线程建立连接
// channelFuture.sync();
// Channel channel = channelFuture.channel();
// channel.writeAndFlush("hello world");
//异步处理 addListener
channelFuture.addListener(new ChannelFutureListener() {
// nio建立连接后执行
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
Channel channel = channelFuture.channel();
channel.writeAndFlush("ojbk");
}
});
上边这段程序就是Netty客户端的一段搭建的代码,一句句来看,
new Bootstrap就是开始创建一个启动类,那么这个启动类需要哪些配置呢?
首先,加载事件循环组EventLoopGroup,这里我们可以指定bossEventLoop和WorkerEventLoop,当然,不指定的话可以那么连接读写由同一个事件循环处理了就好,通过链式调用 .group加载进启动类
有group了,那么group管理哪个channel呢?,我们通过 . channel指定管理的channel类型
同时我们还要指定channel在建立连接后会进行哪些处理,需要指定handler
最后在进行connect的一个链接;
这一段结束完之后,我们就创建了一个channelFuture,其实我们见到Future就知道,这其实是一个异步类,因为在整个Netty中,所有的io操作都是异步的,所以我们需要一种用于在之后的某个时间确定其结果的方法,(因为直接将之转为channel并发送数据,我们并不知道是EventLoop的哪个线程来处理的)我们有两种方法,可以将channelFuture进行一个处理,进行收发数据的需要。首先,异步转同步,channelFuture.sysc() 方法就是将channelFuture转为同步方法,随后进行消息发送,当然,异步转同步肯定不是一种很优雅的解决方式,那么怎么优雅呢?,异步处理,怎么异步处理呢?其实Netty的ChannelFuture提供了addListener接口来配置监听,当EventLoop执行完操作,建立好连接后,那么哪个线程执行的,哪个线程来处理listener中的方法,在这个方法里,我们可以去write。值得注意的是,channel的write,也是一个异步操作,当客户端write后,他并不会主动去发送数据,什么时候会发呢?flush触发,也就是说,你可以一个channe不停地write,最后再执行一个flush,将之前的数据发出去,当然,要想马上发,那就是writeAndFlush()
ok,服务端
服务端
先上代码
// 服务器启动,负责组装netty组件
new ServerBootstrap()
.group(new NioEventLoopGroup()) // BossEventloop 处理可连接事件 workEventloop 处理可读事件
.channel(NioServerSocketChannel.class) // 选择服务器得ServerSocketChannel实现
// 决定worker进行哪些操作
// ChannelInitializer 初始化channel
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
nioSocketChannel.pipeline().addLast(new StringDecoder()); // bytebuf转字符串
//自定义handler
nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
super.channelRead(ctx, msg);
//ctx.fireChannelRead(msg);//交给后续handler执行
}
});
}
})
.bind(8080);
这个,得和client对照着看,启动类不一样,服务端启的是ServerBootStrap(),后边的group,channel和childHandler都一样,那么服务端主要做一个消息的接收,怎么处理?就是利用childHandler去注册handler接收信息,比如说上段代码的new ChannelInitializer里边的initChannel,管道建立初始化的时候怎么做?EventLoop执行哪些handler?handler中做什么事?这些我们都在initChannel方法中去处理
nioSocketChannel.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(msg);
super.channelRead(ctx, msg);
//ctx.fireChannelRead(msg);//交给后续handler执行
}
});
这一段,就是想socketchannel中添加了一个接收并处理消息的handler。
ok,发完了,接收到了,怎么关呢?
关闭客户端
我们都知道,Netty处理IO操作都是一步的,那么我们能直接在主程序里边去channe.close()吗?肯定不行,关不掉,那么怎么处理?首选,同步关闭
channel.closeFuture().sysc()
这一接口就是将channel转成同步,阻塞主线程,等待eventLoop执行完毕,进行后续操作
在这之后,worker.gracefullyShutdow() 优雅结束
二,异步处理
ChannelFuture closeFuture = channel.closeFuture();
closeFuture.addListener((future)->{
log.debug("closingggggggggg");
eventExecutors.shutdownGracefully();//group结束
});
还是addListener接口处理,哪个线程执行完了,优雅关闭。
当然,上边客户端,服务端的代码都是用了匿名内部类,只有一个方法,当然可以用lambda表达式去优化。
代码仅供批判