springboot整合netty实现tcp通信

本文详细介绍了如何使用Netty搭建服务器,包括依赖配置、EventLoopGroup管理、ChannelInitializer处理客户端连接,以及使用LengthFieldBasedFrameDecoder解决粘包问题。同时涵盖了如何启用TCP Keepalive功能,以及IdleStateHandler进行心跳检测和空闲连接管理。

1、依赖

<!-- netty依赖-->
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.0.23.Final</version>
</dependency>

2、搭建 Netty 服务端

Netty服务器:NettyServer

public class NettyServer {

    public NettyServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        new EchoServer(8888).start(); // 启动
    }

    public void start() {
        // 负责连接请求
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        // 负责事件响应
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // 服务器启动项
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            // 临时存放已完成三次握手的请求的队列的最大长度。
            // 如果未设置或所设置的值小于1,Java将使用默认值50。
            // 如果大于队列的最大长度,请求会被拒绝
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
            // 保持长连接状态
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            //绑定线程池:handler是针对bossGroup,childHandler是针对workerHandler
            serverBootstrap.group(bossGroup, workerGroup)
                    // 选择nioChannel
                    .channel(NioServerSocketChannel.class)
                    // 绑定监听端口
                    .localAddress(this.port)
                    // 绑定客户端连接时候触发操作
                    .childHandler(new ChannelInitializer<SocketChannel>() { 
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            logger.info("新增客户端连接,IP:" + ch.localAddress().getHostName() + ",Port:" + ch.localAddress().getPort());
                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(1024, 22, 2, 1, 0));
                            ch.pipeline().addLast(new ByteArrayEncoder());// 编码(发送数据)
                            ch.pipeline().addLast(new ByteArrayDecoder());// 解码(接受数据)
                            ch.pipeline().addLast(new NettyServerHandler()); // 客户端触发操作
                        }
                    });

            // 服务器异步创建绑定定
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            //该方法进行阻塞,等待服务端链路关闭之后继续执行。
            //这种模式一般都是使用Netty模块主动向服务端发送请求,然后最后结束才使用
            channelFuture.channel().closeFuture().sync();
        } finally {
            // 释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

Netty处理器:NettyServerHandler

@Component
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

	// 是否登出操作
    private Boolean isLogout;

	//region 使用依赖注入
	private static NettyServerHandler nettyServerHandler;
	
    @Autowired
    MyService myService;

    public NettyServerHandler() {
    }

    @PostConstruct
    public void init() {
        nettyServerHandler = this;
    }
	//region 
 
    /**
	 * channel 通道 action 活跃
     * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("客户端连接成功:" + ctx.channel().localAddress().toString() + " 通道已激活!");
    }
 
    /**
	 * channel 通道 action 不活跃
     * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("客户端断开连接:" + ctx.channel().localAddress().toString() + " 通道不活跃!");
		// channel失效,从Map中移除
        NettyChannelMap.removeValue((SocketChannel) ctx.channel());
        // 关闭流
        ctx.close();
    }
 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		logger.info("服务器:收到客户端数据");
		try {
			byte[] msgByte = (byte[]) msg;
            // TODO 解析数据包
            DataPackage dataPackage = nettyServerHandler.myService.baseAnalysis(msgByte);
            // 唯一标识码            
            String uuid = dataPackage.getUuid();
			// 将channel保存到内存
            NettyChannelMap.add(uuid, (SocketChannel) channelHandlerContext.channelHandlerContext.channel());
			// TODO 其他操作
            nettyServerHandler.myService.analysis(msgByte);
			//返回应答
            NettyChannelMap.get(uuid).writeAndFlush(response_msg);
		 } finally {
            ReferenceCountUtil.release(msg);
        }
    }
	
	/**
     * 读取完毕客户端发送过来的数据之后的操作
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        logger.info("服务端:接收数据处理完毕");
        // 登出:写一个空的buf,并刷新写出区域。完成后关闭sock channel连接。
        if (th
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值