Netty主要使用Bootstrap、ChannelPipeline、Handler 、EventLoop、ByteBuf这几个类
基本概念见:https://blog.csdn.net/crazymakercircle/article/details/84332086#Netty_Bootstrap
channelPipiline设计见:https://blog.csdn.net/weixin_39818173/article/details/86700313
ClientChannelInitializer.java
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
/**
* 初始化channel
* */
public class ClientChannelInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Unpooled.copiedBuffer("$_".getBytes())));
pipeline.addLast("decode",new StringDecoder());
pipeline.addLast("encode",new StringEncoder());
pipeline.addLast(new IdleStateHandler(0,30,0,TimeUnit.SECONDS));
pipeline.addLast("handler",new NettyClientHandler());
}
}
NettyClient.java
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class NettyClient {
static NettyClient nettyClient;
static final String HOST = "127.0.0.1";
static final int PORT = 8000;
public void start(){
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
//保持长连接 CHannelOption.SO_KEEPALIVE
b.group(group)
.option(ChannelOption.SO_KEEPALIVE,true)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
final ChannelFuture f = b.connect(HOST,PORT);
//断线重连
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
//channelFuture is false
if(!future.isSuccess()){
final EventLoop loop = future.channel().eventLoop();
loop.schedule(new Runnable() {
@Override
public void run() {
log.error("服务器连接不上,开始重连操作...");
start();
}
},1L,TimeUnit.SECONDS);
}
}
});
}
public static void main(String...args){
nettyClient = new NettyClient();
nettyClient.start();
}
}
NettyClientHandler.java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler {
//1 判断存活 2 Read 3 readcomplete
//channel读取时调用的方法
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("Server say :"+msg.toString());
}
//channel存活时调用的方法
@Override
public void channelActive(ChannelHandlerContext ctx){
log.info("已连接");
}
//channel待用时调用
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("断线了。。。。。。");
//使用过程中断线重连
final EventLoop eventLoop = ctx.channel().eventLoop();
eventLoop.schedule(new Runnable() {
@Override
public void run() {
NettyClient.nettyClient.start();
}
}, 1, TimeUnit.SECONDS);
ctx.fireChannelInactive();
}
//用户事件触发
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
//IdleState.READER_IDLE没有收到数据
if (event.state().equals(IdleState.READER_IDLE)) {
System.out.println("READER_IDLE");
//IdleState.READER_IDLE没有数据发送
} else if (event.state().equals(IdleState.WRITER_IDLE)) {
/**发送心跳,保持长连接*/
String s = "ping$_";
ctx.channel().writeAndFlush(s);
log.debug("心跳发送成功!");
System.out.println("心跳发送成功!");
//暂时没没有收到或发送任何数据
} else if (event.state().equals(IdleState.ALL_IDLE)) {
System.out.println("ALL_IDLE");
}
}
super.userEventTriggered(ctx, evt);
}
}
NettyServer.java
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServer {
static final int PORT =8000;
public void bind() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new NettyServerChannelInitializer());
ChannelFuture f = b.bind(PORT).sync();
//等待服务器监听端口关闭
f.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
public static void main(String... arg) throws InterruptedException {
new NettyServer().bind();
}
}
NettyServer
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServer {
static final int PORT =8000;
public void bind() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new NettyServerChannelInitializer());
ChannelFuture f = b.bind(PORT).sync();
//等待服务器监听端口关闭
f.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
public static void main(String... arg) throws InterruptedException {
new NettyServer().bind();
}
}
ChannelInitializer.java
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import java.util.concurrent.TimeUnit;
public class NettyServerChannelInitializer extends ChannelInitializer {
private NettyServerHandler handler ;
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//解决TCP粘包拆包的问题,以特定的字符结尾($_)
pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("$_".getBytes())));
//字符串解码和编码
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast(new IdleStateHandler(40,0,0,TimeUnit.SECONDS));
//服务器的逻辑
handler = new NettyServerHandler();
pipeline.addLast("handler", handler);
}
}
NettyServerHandler.java
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler {
private int counter = 0;
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
//成功读取计数为0
log.info("Client say: "+msg.toString());
counter=0;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("RemoteAddress : "+ctx.channel().remoteAddress().toString());
super.channelActive(ctx);
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof IdleStateEvent){
IdleStateEvent event = (IdleStateEvent) evt;
if(event.state().equals(IdleState.READER_IDLE)){
//空闲40s后触发丢失
}
if(counter >=3){
//连续丢失3次 断开连接
ctx.channel().close().sync();
log.info("已于"+ctx.channel().remoteAddress()+"断开连接");
}else {
counter++;
log.info(ctx.channel().remoteAddress()+"丢失了"+counter+"个包");
}
}
}
}