1、 BIO/NIO/AIO
服务端
package com.weyne.io.heartbeat.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeartBeatServer {
private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatServer.class);
public static void main(String[] args) {
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new IdleStateHandler(3, 0, 0));
ch.pipeline().addLast(new HeartBeatServerHandler());
}
});
LOGGER.info("netty server start...");
ChannelFuture future;
try {
future = bootstrap.bind(8090).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
work.shutdownGracefully();
boss.shutdownGracefully();
}
}
}
HeartBeatServerHandler
package com.weyne.io.heartbeat.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeartBeatServer {
private static final Logger LOGGER = LoggerFactory.getLogger(HeartBeatServer.class);
public static void main(String[] args) {
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, work)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new IdleStateHandler(3, 0, 0));
ch.pipeline().addLast(new HeartBeatServerHandler());
}
});
LOGGER.info("netty server start...");
ChannelFuture future;
try {
future = bootstrap.bind(8090).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
work.shutdownGracefully();
boss.shutdownGracefully();
}
}
}
客户端
package com.weyne.io.heartbeat.client;
import com.weyne.io.heartbeat.server.HeartBeatServerHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Random;
public class HeartBeatClient {
private static final Logger logger = LoggerFactory.getLogger(HeartBeatServerHandler.class);
public static void main(String[] args) {
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("encoder" , new StringEncoder());
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast(new HeartBeatClientHandler());
}
});
logger.info("netty client start...");
try {
Channel channel = bootstrap.connect("127.0.0.1", 8090).sync().channel();
String text = "Heartbeat Packet";
Random random = new Random();
while (channel.isActive()){
int num = random.nextInt(10);
logger.info(String.format("[%s]后发起心跳检测...", num));
Thread.sleep(3 * 1000);
channel.writeAndFlush(text);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
eventLoopGroup.shutdownGracefully();
}
}
}
HeartBeatClientHandler
package com.weyne.io.heartbeat.client;
import com.weyne.io.heartbeat.server.HeartBeatServerHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeartBeatClientHandler extends SimpleChannelInboundHandler<String> {
private static final Logger logger = LoggerFactory.getLogger(HeartBeatServerHandler.class);
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
logger.info("client recived:"+msg);
if (msg != null && msg.equals("idle close")){
logger.info("服务端关闭连接,客户端关闭");
ctx.channel().closeFuture();
}
}
}
源码解读:
pipeline加入IdleStateHandler心跳连接核心组件,查看该类,继承了ChannelDuplexHandler,该类不仅继承了ChannelInboundHandlerAdapter,还实现了ChannelOutboundHandler,也就是入站和出站的数据流都需要经过这个handler。
/**
* {@link ChannelHandler} implementation which represents a combination out of a {@link ChannelInboundHandler} and
* the {@link ChannelOutboundHandler}.
*
* It is a good starting point if your {@link ChannelHandler} implementation needs to intercept operations and also
* state updates.
*/
public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler {
... ...
}
查看IdleStateHandler的channelActive方法,关键在于initialize(ctx)
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// This method will be invoked only if this handler was added
// before channelActive() event is fired. If a user adds this handler
// after the channelActive() event, initialize() will be called by beforeAdd().
initialize(ctx);
super.channelActive(ctx);
}
接着在该方法中,根据IdleState实现不同的读/写/读写超时task
private void initialize(ChannelHandlerContext ctx) {
// Avoid the case where destroy() is called before scheduling timeouts.
// See: https://github.com/netty/netty/issues/143
switch (state) {
case 1:
case 2:
return;
}
state = 1;
initOutputChanged(ctx);
lastReadTime = lastWriteTime = ticksInNanos();
if (readerIdleTimeNanos > 0) {
readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx),
readerIdleTimeNanos, TimeUnit.NANOSECONDS);
}
if (writerIdleTimeNanos > 0) {
writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx),
writerIdleTimeNanos, TimeUnit.NANOSECONDS);
}
if (allIdleTimeNanos > 0) {
allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
allIdleTimeNanos, TimeUnit.NANOSECONDS);
}
}
继续跟到ReaderIdleTimeoutTask,该类最后实现的是Runnable,看run()方法,关键在于channelIdle(ctx, event);将channel和IdleState传递下去
private final class ReaderIdleTimeoutTask extends AbstractIdleTask {
ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
super(ctx);
}
@Override
protected void run(ChannelHandlerContext ctx) {
long nextDelay = readerIdleTimeNanos;
if (!reading) {
nextDelay -= ticksInNanos() - lastReadTime;
}
if (nextDelay <= 0) {
// Reader is idle - set a new timeout and notify the callback.
readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS);
boolean first = firstReaderIdleEvent;
firstReaderIdleEvent = false;
try {
IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first);
channelIdle(ctx, event);
} catch (Throwable t) {
ctx.fireExceptionCaught(t);
}
} else {
// Read occurred before the timeout - set a new timeout with shorter delay.
readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
}
}
}
继续下去看到ctx.fireUserEventTriggered(evt);看到fire,就要联想到这是执行到pipeline中的下个channel了,而本例中的下一个channel是HeartBeatServerHandler,所以直接到HeartBeatServerHandler.userEventTriggered了。
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
ctx.fireUserEventTriggered(evt);
}
那是怎么指定下一个handler的呢?我们继续看ctx.fireUserEventTriggered(evt);看findContextInbound(MASK_USER_EVENT_TRIGGERED)是找到当前ChannelHandlerContext的下一个handle,直到什么条件才最终完成赋值,这块先不管,主要是ctx = ctx.next赋值下一个handlerContext为当前ctx 。源码如下:
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
EventExecutor currentExecutor = executor();
do {
ctx = ctx.next;
} while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND));
return ctx;
}
再看invokeUserEventTriggered(findContextInbound(MASK_USER_EVENT_TRIGGERED), event);是反射初始化类,然后执行方法userEventTriggered
static void invokeUserEventTriggered(final AbstractChannelHandlerContext next, final Object event) {
ObjectUtil.checkNotNull(event, "event");
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeUserEventTriggered(event);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeUserEventTriggered(event);
}
});
}
}
private void invokeUserEventTriggered(Object event) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).userEventTriggered(this, event);
} catch (Throwable t) {
invokeExceptionCaught(t);
}
} else {
fireUserEventTriggered(event);
}
}