import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.util.CharsetUtil; import lombok.SneakyThrows; import java.net.InetSocketAddress; public class BootNettyUdpClient { private int port; private String server; public BootNettyUdpClient(String server,int port){ this.server = server; this.port = port; } @SneakyThrows public void send(String data) { data = AES.encryptAES(data); EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try { Bootstrap clientBootstrap = new Bootstrap(); clientBootstrap = clientBootstrap.group(eventLoopGroup); clientBootstrap = clientBootstrap.channel(NioDatagramChannel.class); clientBootstrap = clientBootstrap.option(ChannelOption.SO_BROADCAST, true); clientBootstrap = clientBootstrap.handler(new BootNettyUdpClientSimpleChannelInboundHandler()); Channel channel = clientBootstrap.bind(0).sync().channel(); channel.writeAndFlush(new DatagramPacket( Unpooled.copiedBuffer(data, CharsetUtil.UTF_8), new InetSocketAddress(server,port))); System.out.println("channnel id = "+channel.id().toString()); channel.closeFuture().await(); } catch (Exception e) { // TODO: handle exception } finally { System.out.println("netty client udp close!"); eventLoopGroup.shutdownGracefully(); } } }
在你提供的 BootNettyUdpClient
类中,send
方法每次调用都会创建一个新的 EventLoopGroup
、Bootstrap
实例,并且绑定一个新的 Channel
到一个临时的本地端口(通过 bind(0)
),然后发送数据包。这种设计有几个问题,其中最主要的是每次发送都会启动一个新的 Netty 客户端实例,并且在发送完数据后等待这个客户端实例关闭(channel.closeFuture().await();
)。
这就是为什么你只能发送一次数据,并且再次调用 send
方法时会阻塞:
资源消耗:每次发送都创建和销毁大量的 Netty 组件,这是非常低效的。
阻塞:
channel.closeFuture().await();
会阻塞当前线程,直到这个Channel
关闭。由于你在发送完数据后立即关闭了Channel
,所以这个方法调用会等待直到 Netty 完成所有的清理工作。这意味着在send
方法返回之前,不会有其他数据被发送。无法重用连接:UDP 本身是无连接的协议,但 Netty 的
DatagramPacket
允许你在同一个Channel
上发送多个数据包到不同的目标地址。然而,你的代码每次都会创建一个新的Channel
,因此无法重用之前的连接(尽管 UDP 实际上没有“连接”的概念,但这里指的是 Netty 的Channel
实例)。
为了解决这个问题,你可以考虑以下方案:
重用
EventLoopGroup
和Channel
:将EventLoopGroup
和Channel
的创建移到BootNettyUdpClient
类的构造函数或某个初始化方法中,然后在send
方法中只使用这些已经创建好的资源来发送数据。移除
channel.closeFuture().await();
:由于你不需要等待客户端关闭,可以直接发送数据后返回。Netty 会自动管理Channel
的生命周期,特别是在使用无连接的 UDP 协议时。
下面是一个简化的示例,展示了如何修改你的代码以重用 EventLoopGroup
和 Channel
:
java复制代码
public class BootNettyUdpClient { private final int port; private final String server; private final EventLoopGroup eventLoopGroup; private final Channel channel;
public BootNettyUdpClient(String server, int port) throws InterruptedException { this.server = server; this.port = port; this.eventLoopGroup = new NioEventLoopGroup();
Bootstrap clientBootstrap = new Bootstrap(); clientBootstrap.group(eventLoopGroup) .channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .handler(new BootNettyUdpClientSimpleChannelInboundHandler());
this.channel = clientBootstrap.bind(0).sync().channel(); }
@SneakyThrows public void send(String data) { data = AES.encryptAES(data); channel.writeAndFlush(new DatagramPacket( Unpooled.copiedBuffer(data, CharsetUtil.UTF_8), new InetSocketAddress(server, port))); System.out.println("Data sent to " + server + ":" + port); }
public void shutdown() throws InterruptedException { channel.close().sync(); eventLoopGroup.shutdownGracefully().sync(); System.out.println("netty client udp close!"); } }
在这个修改后的版本中,EventLoopGroup
和 Channel
只在构造函数中创建一次,并且提供了一个 shutdown
方法来优雅地关闭这些资源。现在你可以多次调用 send
方法来发送数据,而不会遇到阻塞或资源耗尽的问题。记得在不再需要客户端时调用 shutdown
方法来释放资源。
0条评论
我要评论