Netty学习(五):Netty实现UDP的Server端

该博客展示了如何使用Spring Boot结合Netty实现一个UDP监听服务。通过`CommandLineRunner`接口启动服务,配置了UDP端口和URL,利用`NioEventLoopGroup`、`Bootstrap`和`SimpleChannelInboundHandler`处理网络事件。服务端接收到数据后,进行解析和处理,并具备异常处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主启动

@SpringBootApplication
public class UdpListenerApplication implements CommandLineRunner {
    @Value("${netty.port}")
    private int port;

    @Value("${netty.url}")
    private String url;

    @Autowired
    private BootNettyUdpServer bootNettyUdpServer;

    @Override
    public void run(String... args) throws Exception {
        /**
         * 使用异步注解方式启动netty udp服务端服务
         */
        InetSocketAddress address = new InetSocketAddress(url, port);
        System.out.println("服务端启动成功:" + url + ":" + port);
        bootNettyUdpServer.start(address);

    }

    public static void main(String[] args) {
        SpringApplication.run(UdpListenerApplication.class, args);
        System.out.println("UdpListenerApplication启动成功......");
    }
}

Server

@Service
public class BootNettyUdpServer {
    //logger
    private static final Logger log = LoggerFactory.getLogger(BootNettyUdpServer.class);

    @Autowired
    BootNettyUdpSimpleChannelInboundHandler bootNettyUdpSimpleChannelInboundHandler;

    /**
     * 启动服务
     */
    public void start(InetSocketAddress address) {

        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();//
            b.group(workerGroup).channel(NioDatagramChannel.class)//设置UDP通道
                   // .handler(new BootNettyUdpSimpleChannelInboundHandler())//初始化处理器
                    .handler(bootNettyUdpSimpleChannelInboundHandler)//初始化处理器
                    .option(ChannelOption.SO_BROADCAST, true)// 支持广播
                    .option(ChannelOption.SO_RCVBUF, 1024 * 1024)// 设置UDP读缓冲区为1M
                    .option(ChannelOption.SO_SNDBUF, 1024 * 1024);// 设置UDP写缓冲区为1M

            System.out.println("[UDP 启动了]");

            // 绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(address).sync();
            //让线程进入wait状态,也就是main线程暂时不会执行到finally里面,nettyserver也持续运行,如果监听到关闭事件,可以优雅的关闭通道和nettyserver
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            // TODO: handle exception
            System.out.println("Exception"+e.toString());
            workerGroup.shutdownGracefully();
        }
    }
}

Handler

@Component
public class BootNettyUdpSimpleChannelInboundHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    private static final Charset CHARSET = Charset.forName("GBK");
    private static Logger logger = LoggerFactory.getLogger(BootNettyUdpSimpleChannelInboundHandler.class);

    @Autowired
    ParserManager parserManager;


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception {
        try {
            logger.info("服务器接收到了数据");
            ByteBuf byteBuf = datagramPacket.copy().content();
            logger.info("接收到的数据,hexDump:{}", ByteBufUtil.hexDump(byteBuf));

            Frame frame = new Frame();

            int len = getFrameLength(byteBuf, frame);
            logger.info("帧长度为", len);

            if (len == 0) {
                logger.info("帧长度为", 0);
                return;
            } else if (len < 0) {
                logger.info("帧长度小于0,无效帧");
                // may be not a valid frame
                logger.error("recv a invalid frame, peer ip is: {}, close channel: {}, recv buffer: {}", ctx.channel().remoteAddress(), ctx.channel(), ByteBufUtil
                        .hexDump(byteBuf));

                byteBuf.skipBytes(byteBuf.readableBytes());
                return;
//                ctx.close();
            } else {
                if (!ctx.channel().isActive()) {
                    logger.info("channel is not active");
                }
                if (len > 1024) {
                    logger.error("frame len exceed limit len 1024, close it");
                    byteBuf.skipBytes(byteBuf.readableBytes());
//                    ctx.close();
                    return;
                }
                frame.setFrameLength(len);

                frame.setInsertTime(DateUtils.currentDate());

                frame.setCtx(ctx);

                frame.setDatagramPacket(datagramPacket);
                byte[] body = ByteBufUtil.getBytes(byteBuf, byteBuf.readerIndex() + 4, len - 6);
                frame.setOrigin(body);
                frame.setEnder(byteBuf.getUnsignedShort(byteBuf.readerIndex() + (len - 2)));

                if (frame != null) {
                    parserManager.parser(frame);
                    logger.info("frame is {}", frame);
                } else {
                    byteBuf.release();
                    logger.warn("newRequestFrame return a null frame, close channel");
                    ctx.close();
                }
                byteBuf.skipBytes(len);
            }

////            //收到udp消息后,可通过此方式原路返回的方式返回消息,例如返回时间戳
//            ctx.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("receice data", CharsetUtil.UTF_8),
//                    datagramPacket.sender()));
        } catch (Exception e) {
            logger.error("BootNettyUdpSimpleChannelInboundHandler.channelRead0发生异常", e);
        }
    }

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值