pom依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
Netty服务器配置
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
/**
* NettyServer Netty服务器配置
* @author
* @date
*/
public class NettyServer {
private static Logger logger = LoggerFactory.getLogger(NettyServer.class);
private final int port;
private EventLoopGroup bossGroup = new NioEventLoopGroup();
private EventLoopGroup group = new NioEventLoopGroup();
private ChannelFuture cf;
public NettyServer(int port) {
this.port = port;
}
public void start() throws Exception {
bossGroup = new NioEventLoopGroup();
group = new NioEventLoopGroup();
ServerBootstrap sb = new ServerBootstrap();
sb.option(ChannelOption.SO_BACKLOG, 20);
sb.group(group, bossGroup) // 绑定线程池
.channel(NioServerSocketChannel.class) // 指定使用的channel
.localAddress(this.port)// 绑定监听端口
.childHandler(new ChannelInitializer<SocketChannel>() { // 绑定客户端连接时候触发操作
@Override
protected void initChannel(SocketChannel ch) throws Exception {
logger.info("收到新的客户端连接: {}",ch.toString());
//websocket协议本身是基于http协议的,所以这边也要使用http解编码器
ch.pipeline().addLast(new HttpServerCodec());
//心跳检测,参数说明:[长时间未写:长时间未读:长时间未读写:时间单位]~[读写是对连接本生而言,写:未向服务端发送消息,读:未收到服务端的消息]
ch.pipeline().addLast(new IdleStateHandler(0,5*60,0, TimeUnit.SECONDS));
ch.pipeline().addLast(new IdleStateHandlerInitializer());
//以块的方式来写的处理器
ch.pipeline().addLast(new ChunkedWriteHandler());
ch.pipeline().addLast(new HttpObjectAggregator(8192));
ch.pipeline().addLast(new ChatWebSocketHandler());
//最后一个参数为数据包大小
ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));
}
});
cf = sb.bind().sync(); // 服务器异步创建绑定
logger.info(NettyServer.class + " 启动正在监听: " + cf.channel().localAddress());
}
public void destroy() {
logger.info(NettyServer.class + " netty服务监听关闭: " + cf.channel().localAddress());
try {
cf.channel().closeFuture().sync();// 关闭服务器通道
} catch (Exception e) {
e.printStackTrace();
}finally{
bossGroup.shutdownGracefully().syncUninterruptibly();
group.shutdownGracefully().syncUninterruptibly();
}
}
}
注意:
cf.channel().closeFuture().sync(); // 关闭服务器通道 ~~~~~会阻塞主进程,一般用于单元测试
心跳检测
import java.util.Collection;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
public class IdleStateHandlerInitializer extends ChannelInboundHandlerAdapter {
private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("HEARTBEAT", CharsetUtil.UTF_8));
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if(evt instanceof IdleStateEvent) {
ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); // 关闭连接
} else {
// 传递给下一个处理程序
super.userEventTriggered(ctx, evt);
}
}
}
使用监听器启动
import java.util.Calendar;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NettyServerListener implements ServletContextListener{
private static final Logger logger = LoggerFactory.getLogger(NettyServerListener.class);
private NettyServer nettyServer;
@Override
public void contextInitialized(ServletContextEvent sce) {
nettyServer = new NettyServer(8091);
try {
nettyServer.start();
} catch (Exception e) {
logger.error("netty server abnormal startup {netty服务启动异常}", e);
}
}
/**
* @Author 销毁
* @Date 2020年8月27日 上午10:57:53
* @param sce
* @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("destroy() of netty"+ Calendar.getInstance().getTime()+" flag:"+(null==nettyServer));
nettyServer.destroy();
}
}
装配用于启动netty服务的监听器
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer{
/**
*
* @Description 配置用于启动netty服务的监听器
* @Author xd
* @Date 2020年8月27日 上午10:13:47
* @return
* @return ServletListenerRegistrationBean 返回类型
*/
@Bean(name = "nettyServerListener")
public ServletListenerRegistrationBean<NettyServerListener> nettyListenerRegist() {
ServletListenerRegistrationBean<NettyServerListener> srb = new ServletListenerRegistrationBean<NettyServerListener>();
srb.setListener(new NettyServerListener());
return srb;
}
}