1、添加依赖
<!--netty-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
<scope>compile</scope>
</dependency>
2、创建服务启动类
@Slf4j
@Component
public class TcpServer {
private static class SingletonTcpServer {
static final TcpServer instance = new TcpServer();
}
public static TcpServer getInstance() {
return SingletonTcpServer.instance;
}
public void start(String ip, int port) {
// master reactor
EventLoopGroup bossGroup = new NioEventLoopGroup();
// sub reactor
EventLoopGroup workGroup = new NioEventLoopGroup();
ChannelFuture f;
try {
// start
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup) // install NioEventLoopGroup
.channel(NioServerSocketChannel.class) // set channel type NIO
// set connect params
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childOption(ChannelOption.TCP_NODELAY, true)
// set in/out event handler
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) {
// 添加分隔符解码器
ch.pipeline().addFirst(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("&&".getBytes())));
ch.pipeline().addLast(new TcpActiveHandler());
ch.pipeline().addLast(new TcpMsgInHandler());
}
});
// bind port
f = bootstrap.bind(ip, port).sync();
if (f.isSuccess()) {
log.info("start tcp server success: {}", port);
} else {
log.warn("start tcp server failed: {}", f.cause().getMessage());
}
f.channel().closeFuture().sync();
} catch (Exception e) {
log.error("start tcp server error: {}", e.getMessage());
} finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
3、创建处理器
@Slf4j
@ChannelHandler.Sharable
public class TcpActiveHandler extends ChannelInboundHandlerAdapter {
private final static AtomicLong activeCount = new AtomicLong(0);
@Override
public void channelActive(ChannelHandlerContext ctx) {
log.info("active client count: {}", activeCount.addAndGet(1));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
log.info("active client count: {}", activeCount.decrementAndGet());
}
}
@Slf4j
public class TcpMsgInHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
log.info("client active: {}", ctx.channel().remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
log.info("client inactive: {}", ctx.channel().remoteAddress());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
String readMsg = byteBuf.toString(CharsetUtil.UTF_8);
// 业务代码
ProduceMsg produceMsg = null;
try {
produceMsg = JSONObject.parseObject(readMsg, ProduceMsg.class);
} catch (Exception e) {
log.error("ProduceMsg 格式化消息失败!{}", e.getMessage());
ctx.write(ERROR_FORMAT_MSG);
}
if (produceMsg != null) {
log.info("Client Msg: {}", produceMsg);
TestRecordTopVO testRecordTopVO;
String msgType = produceMsg.getMsgType();
String msgContent = produceMsg.getMsgContent();
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error("client {} exception: {}", ctx.channel().remoteAddress(), cause);
}
}
@Data
public class ProduceMsg {
/**
* 消息类型
*/
private String msgType;
/**
* 消息内容
*/
private String msgContent;
}
4、创建Spring启动监听器
/**
* ContextRefreshedEvent:
* ApplicationContext被初始化或刷新时,该事件被发布。这也可以在ConfigurableApplicationContext接口中使用 refresh() 方法来发生。
* 此处的初始化是指:所有的Bean被成功装载后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用
*/
@Component
public class MainboardServerConfig implements ApplicationListener<ContextRefreshedEvent> {
@Value("${netty.ip}")
private String ip;
@Value("${netty.port}")
private int tcpPort;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ExecutorService service;
service = Executors.newSingleThreadExecutor();
service.submit(() -> TcpServer.getInstance().start(ip, tcpPort));
}
}
最后运行SpringBoot项目即可!