启动接口
public interface StartAble {
/**
* 启动
*/
void on();
/**
* 停止
*/
void off();
}
NioSocketServer
@Slf4j
public class NioSocketServer implements Runnable , StartAble {
public NioSocketServer( ) {
}
NioEventLoopGroup boss ;
NioEventLoopGroup works ;
Channel channel;
@Override
public void run() {
on();
}
@Override
public void on() {
boss = new NioEventLoopGroup();
works = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
//组装线程池用来处理 channel 连接
.group(boss,works)
.channel(NioServerSocketChannel.class)
//设置tcp 队列长度 阻塞队列 + 接受 队列 = so_backlog 长度 说明:https://www.jianshu.com/p/e6f2036621f4
.option(ChannelOption.SO_BACKLOG,1024)
//子配置项 是否保持tcp 长连接(心跳包监听) 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
.childOption(ChannelOption.SO_KEEPALIVE,true)
//子配置项 是否立即发送数据 (立即 或者攒够一定数据)
.childOption(ChannelOption.TCP_NODELAY,true)
//子处理项
.childHandler(new WorksChannelInitializer());
try {
System.out.println("----------");
ChannelFuture future = serverBootstrap.bind(new InetSocketAddress("127.0.0.1",13140)).sync();
channel = future.channel();
log.info("服务器启动开始监听端口: {}", 9999);
/*
* ChannelFuture 保存异步执行结果
* closeFuture() : 如果关闭则返回future实例 (始终返回相同的实例)
* sync() : 等待这个异步执行完成 ,如果异步执行失败 ,则返回异常;
*/
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
off();
}
}
@Override
public void off() {
if(boss !=null){
boss.shutdownGracefully();
}
if(works!=null){
works.shutdownGracefully();
}
if(channel !=null){
channel.close();
}
}
}
WorksChannelInitializer
@Slf4j
public class WorksChannelInitializer extends ChannelInitializer<SocketChannel> {
/**
* 创建处理网络事件的ChannelPipeline和handler,
* 网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码(粘包 拆包) SSl安全认证,
* https://blog.csdn.net/jinxyan/article/details/89712997
*/
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new WorksChannelHandler());
}
/*
* 如果是 proto
* final ChannelPipeline pipeline = socketChannel.pipeline();
* pipeline.addLast(new ProtobufVarint32FrameDecoder());
* pipeline.addLast(new ProtobufDecoder(Proto.Message.getDefaultInstance()));
* pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
* pipeline.addLast(new ProtobufEncoder());
* pipeline.addLast(new NettyServerHandler());
*/
}
WorksChannelHandler
@Slf4j
public class WorksChannelHandler extends SimpleChannelInboundHandler<String> {
/**
* 用来存储会话
*/
private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final Channel channel = ctx.channel();
log.info("【{}】连接到服务器",ctx.channel().id());
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext,String msg) throws Exception {
log.info("【{}】:{}",channelHandlerContext.channel().id(), JSON.toJSONString(msg));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("[{}]断开来连接",ctx.channel().id()) ;
}
/**
* 发生异常触发
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.info("【{}】出现异常",ctx.channel().id());
cause.printStackTrace();
ctx.close();
}
}