5.Netty UDP服务端(UdpServer)

目录


Netty专栏目录(点击进入…)


Netty UDP服务端(UdpServer)

Reactor Netty 提供了易于使用和易于配置的UdpServer. 它隐藏了创建UDP服务器所需的大部分Netty功能并增加了Reactive Streams背压(Reactive Streams是具有无阻塞背压的异步流处理的标准)

启动和停止

要启动UDP服务器,必须创建和配置UdpServer实例

默认情况下:主机:localhost、端口:12012

创建和启动UDP服务器:
返回的Connection提供了一个简单的服务器API,服务器主机localhost、端口8080;包括disposeNow(),它以阻塞方式关闭服务器。

import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()  // 创建一个UdpServer准备好配置的实例
                           .host("localhost")  // 配置UDP服务器主机
				          .port(8080)     // 配置UDP服务器端口            
				         .bindNow(Duration.ofSeconds(30)); // 以阻塞方式启动服务器并等待它完成初始化

		server.onDispose()
		      .block();
	}
	
}

急切初始化

默认情况下,UdpServer资源的初始化按需进行。这意味着绑定操作会占用初始化和加载所需的额外时间:
(1)事件循环组
(2)本机传输库(使用本机传输时)

当需要预加载这些资源时,可以进行UdpServer如下配置:

import io.netty.channel.socket.DatagramPacket;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;

import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		UdpServer udpServer =
				UdpServer.create()
				         .handle((in, out) ->
				             out.sendObject(
				                 in.receiveObject()
				                   .map(o -> {
				                       if (o instanceof DatagramPacket) {
				                           DatagramPacket p = (DatagramPacket) o;
				                           return new DatagramPacket(p.content().retain(), p.sender());
				                       }
				                       else {
				                           return Mono.error(new Exception("Unexpected type of the message: " + o));
				                       }
				                   })));

		udpServer.warmup()  // 初始化并加载事件循环组和本机传输库
		         .block();

		Connection server = udpServer.bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

写入数据

要将数据发送到远程对等方,必须附加一个I/O处理程序。I/O处理程序有权访问UdpOutbound,以便能够写入数据:

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;

import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
				         .handle((in, out) ->
				             out.sendObject(
				                 in.receiveObject()
				                   .map(o -> {
				                       if (o instanceof DatagramPacket) {
				                           DatagramPacket p = (DatagramPacket) o;
				                           ByteBuf buf = Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8);
                                            // hello向远程对等方发送字符串
				                           return new DatagramPacket(buf, p.sender()); 
				                       }
				                       else {
				                           return Mono.error(new Exception("Unexpected type of the message: " + o));
				                       }
				                   })))
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

消费数据

要从远程对等方接收数据,必须附加一个I/O处理程序。I/O处理程序有权访问UdpInboun,以便能够读取数据:

import io.netty.channel.socket.DatagramPacket;
import reactor.core.publisher.Mono;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;

import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
				         .handle((in, out) ->
				             out.sendObject(
				                 in.receiveObject()
				                   .map(o -> {   // 从远程对等方接收数据
				                       if (o instanceof DatagramPacket) {
				                           DatagramPacket p = (DatagramPacket) o;
				                           return new DatagramPacket(p.content().retain(), p.sender()); 
				                       }
				                       else {
				                           return Mono.error(new Exception("Unexpected type of the message: " + o));
				                       }
				                   })))
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

生命周期回调

提供了以下生命周期回调以让扩展UdpServer:

回调函数描述
doOnBind在服务器通道即将绑定时调用
doOnBound在绑定服务器通道时调用
doOnChannelInit初始化通道时调用
doOnUnbound当服务器通道未绑定时调用

使用doOnBound和doOnChannelInit回调:

import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.logging.LoggingHandler;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
                   // Netty管道LineBasedFrameDecoder在绑定服务器通道时扩展
				  .doOnBound(conn -> conn.addHandler(new LineBasedFrameDecoder(8192))) 
				  .doOnChannelInit((observer, channel, remoteAddress) ->
                    // Netty管道LoggingHandler在初始化通道时扩展
				   channel.pipeline()
				   .addFirst(new LoggingHandler("reactor.netty.examples")))  
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

Option和childOption参数设置

Option和childOption参数设置(点击进入…)


连接配置(三种配置)

(1)Channel Options:通道配置

默认情况下,UDP服务器配置有以下选项:

UdpServerBind() {
	this.config = new UdpServerConfig(
			Collections.singletonMap(ChannelOption.AUTO_READ, false),
			() -> new InetSocketAddress(NetUtil.LOCALHOST, DEFAULT_PORT));
}

如果需要附加选项或需要更改当前选项,可以应用以下配置:

import io.netty.channel.ChannelOption;
import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
				         .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

(2)Wire Logger:连线日志记录

Reactor Netty提供线路日志记录,用于何时需要检查对等点之间的流量。默认情况下,线路日志记录处于禁用状态。要启用它,必须将记录器reactor.netty.udp.UdpServer级别设置为DEBUG并应用以下配置:

import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
				         .wiretap(true)  // 启用连线记录
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

Wire Logger格式化程序
Reactor Netty支持3种不同的格式化程序:

连线日志格式化描述
AdvancedByteBufFormat#HEX_DUM同时记录事件和内容。内容将采用十六进制格式(默认)
AdvancedByteBufFormat#SIMPLE使用此格式启用连线记录时,仅记录事件
AdvancedByteBufFormat#TEXTUAL同时记录事件和内容。内容将采用纯文本格式

(3)Event Loop Group:事件循环组

默认情况下,UDP服务器使用“事件循环组”,其中工作线程的数量等于初始化时运行时可用的处理器数量(但最小值为4)。当需要不同的配置时,可以使用LoopResource#create()方法之一

“事件循环组”的默认配置:

/**
 * 默认工作线程数,回退到可用处理器(但最小值为4)
 */
public static final String IO_WORKER_COUNT = "reactor.netty.ioWorkerCount";
/**
 * 默认选择器线程计数,回退到-1(无选择器线程)
 */
public static final String IO_SELECT_COUNT = "reactor.netty.ioSelectCount";
/**
 *UDP的默认工作线程数,回退到可用处理器(但最小值为4)
 */
public static final String UDP_IO_THREAD_COUNT = "reactor.netty.udp.ioThreadCount";
/**
 * 默认的静默期,保证不会发生对底层循环资源的处置,回退到2秒
 */
public static final String SHUTDOWN_QUIET_PERIOD = "reactor.netty.ioShutdownQuietPeriod";
/**
 *  默认情况下,无论任务是否在静默期内提交,在处理底层资源之前等待的最长时间为15秒。
*/
public static final String SHUTDOWN_TIMEOUT = "reactor.netty.ioShutdownTimeout";
/**
 * 默认值是否首选本机传输(epoll、kqueue),回退在可用时是否首选
 */
public static final String NATIVE = "reactor.netty.native";

需要更改这些设置,可以应用以下配置:

import reactor.netty.Connection;
import reactor.netty.resources.LoopResources;
import reactor.netty.udp.UdpServer;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		LoopResources loop = LoopResources.create("event-loop", 1, 4, true);

		Connection server =
				UdpServer.create()
				         .runOn(loop)
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

Metrics(指标监控)

UDP服务器支持与Micrometer,它公开了前缀为“reactor.netty.udp.server”的所有指标

启用该集成:

import reactor.netty.Connection;
import reactor.netty.udp.UdpServer;

import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
				         .metrics(true)  // 启用与Micrometer的内置集成
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

当需要UDP服务器指标与系统集成时,Micrometer或者想提供自己的集成Micrometer,可以提供自己的指标记录器:

import reactor.netty.Connection;
import reactor.netty.channel.ChannelMetricsRecorder;
import reactor.netty.udp.UdpServer;

import java.net.SocketAddress;
import java.time.Duration;

public class Application {

	public static void main(String[] args) {
		Connection server =
				UdpServer.create()
                     // 启用UDP服务器指标并提供ChannelMetricsRecorder实现
				         .metrics(true, CustomChannelMetricsRecorder::new) 
				         .bindNow(Duration.ofSeconds(30));

		server.onDispose()
		      .block();
	}
	
}

(1)UDP服务器指标的信息

metric名称类型描述
reactor.netty.udp.server.data.receivedDistributionSummary接收的数据量,以字节为单位
reactor.netty.udp.server.data.sentDistributionSummary发送的数据量,以字节为单位
reactor.netty.udp.server.errorsCounter发生的错误数

(2)ByteBufAllocator指标

metric名称类型描述
reactor.netty.bytebuf.allocator.used.heap.memoryGauge堆内存的字节数
reactor.netty.bytebuf.allocator.used.direct.memoryGauge直接内存的字节数
reactor.netty.bytebuf.allocator.used.heap.arenasGauge堆区域的数量(当PooledByteBufAllocator)
reactor.netty.bytebuf.allocator.used.direct.arenasGauge直接竞技场的数量(当PooledByteBufAllocator)
reactor.netty.bytebuf.allocator.used.threadlocal.cachesGauge直接竞技场的数量(当PooledByteBufAllocator)
reactor.netty.bytebuf.allocator.used.small.cache.sizeGauge小缓存的大小(当PooledByteBufAllocator)
reactor.netty.bytebuf.allocator.used.normal.cache.sizeGauge正常缓存的大小(当PooledByteBufAllocator)
reactor.netty.bytebuf.allocator.used.chunk.sizeGauge竞技场的块大小(当PooledByteBufAllocator)

(3)EventLoop指标

metric名称类型描述
reactor.netty.eventloop.pending.tasksGauge事件循环中待处理的任务数
  • 10
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Netty 是一个 Java 开发的网络应用程序框架,提供了许多易于使用的 API 用于开发各种网络应用程序,包括 UDP 服务端。下面是一个使用 Netty 开发 UDP 服务端的简单示例代码: ``` import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioDatagramChannel; public class NettyUDPServer { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioDatagramChannel.class) .handler(new SimpleChannelInboundHandler<DatagramPacket>() { @Override protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { // 处理接收到的数据 } }); ChannelFuture f = b.bind(8080).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } ``` 在上面的代码中,我们创建了一个 Bootstrap 实例并配置了它,将其绑定到 8080 端口。然后,我们在 channelRead0() 方法中处理接收到的数据包。 这仅仅是一个简单的示例,您可以根据需要扩展它并实现更多功能。 ### 回答2: Netty是一个基于Java的网络编程框架,它提供了丰富的API和组件,方便开发者进行TCP和UDP的网络通信。下面是一个简单的Netty UDP服务端实现。 首先,需要引入Netty的相关依赖。可以通过Maven或者手动下载jar包的方式导入。 接着,创建一个UDP服务端类,命名为UdpServer。在这个类中,需要实现对应的ChannelInboundHandlerAdapter来处理传入的UDP数据报。 首先,创建一个Bootstrap实例来设置服务端的配置。然后,设置NioEventLoopGroup来处理I/O操作。对于UDP协议,只需要一个处理I/O的EventLoopGroup就足够了。 然后,配置服务端的Channel。在这里,使用NioDatagramChannel类来创建UDP服务端通道。设置通道的Option参数,如SO_BROADCAST和SO_REUSEADDR,以及绑定服务端的IP地址和端口号。 接下来,创建ChannelInitializer来初始化服务端的ChannelPipeline。在这个方法中,将自定义的ChannelInboundHandlerAdapter添加到ChannelPipeline中。这个Handler负责处理传入的UDP数据报。 最后,绑定并启动服务端,等待客户端的连接。 以下是一个简单的示例代码: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInboundHandlerAdapter; 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; public class UdpServer { private static final int PORT = 9999; public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) .option(ChannelOption.SO_REUSEADDR, true) .handler(new ChannelInitializer<NioDatagramChannel>() { @Override protected void initChannel(NioDatagramChannel ch) throws Exception { ch.pipeline().addLast(new UdpServerHandler()); } }); ChannelFuture future = bootstrap.bind(PORT).sync(); future.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } private static class UdpServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { DatagramPacket packet = (DatagramPacket) msg; // 处理UDP数据报的逻辑 // ... } } } ``` 在上面的代码中,创建了一个UdpServerHandler类来处理传入的UDP数据报。你可以根据自己的需求实现具体的业务逻辑。 以上是一个简单的Netty UDP服务端实现示例,希望对你有所帮助。 ### 回答3: Netty是一个高性能的网络编程框架,支持TCP和UDP协议。下面我将简要介绍如何使用Netty编写一个UDP服务端。 首先,我们需要创建一个新的Java项目,并引入Netty库的相关依赖。可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency> ``` 接下来,我们创建一个UDP服务端的类,命名为"UdpServer"。在该类中,我们需要完成以下几个步骤: 1. 创建一个EventLoopGroup对象。EventLoopGroup用于处理IO操作,默认情况下会创建2 * CPU核心数个EventLoop用于处理任务。 2. 设置服务端的启动类Bootstrap,并配置相关参数,如channel类型、端口号等。 3. 设置ChannelInitializer,用于初始化每个新连接的Channel,将需要处理的Handler添加到Channel的ChannelPipeline中。 4. 启动服务端,绑定并侦听指定的端口。 下面是一个简单的UDP服务端示例代码: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; public class UdpServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); // 创建EventLoopGroup对象 try { Bootstrap bootstrap = new Bootstrap(); // 创建启动类Bootstrap对象 bootstrap.group(group) // 设置EventLoopGroup .channel(NioDatagramChannel.class) // 设置channel类型为NioDatagramChannel .option(ChannelOption.SO_BROADCAST, true) // 设置选项,允许广播 .handler(new ChannelInitializer<DatagramChannel>() { @Override protected void initChannel(DatagramChannel ch) throws Exception { ch.pipeline().addLast(new UdpServerHandler()); // 添加自定义的ChannelHandler } }); ChannelFuture future = bootstrap.bind(9999).sync(); // 启动服务端并绑定端口 future.channel().closeFuture().await(); // 关闭通道的事件监听 } finally { group.shutdownGracefully().sync(); // 关闭EventLoopGroup } } } ``` 在上述代码中,UdpServerHandler为自定义的ChannelHandler,用于处理接收到的UDP消息。你可以在该Handler中编写自己的业务逻辑。 以上是一个简单的Netty UDP服务端实现。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值