如需使用最新稳定版本,请使用 Spring Integration 7.0.4spring-doc.cadn.net.cn

UDP 适配器

本节介绍如何配置和使用 UDP 适配器。spring-doc.cadn.net.cn

出站 UDP 适配器(XML 配置)

以下示例配置了一个 UDP 出站通道适配器:spring-doc.cadn.net.cn

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    socket-customizer="udpCustomizer"
    channel="exampleChannel"/>
当将 multicast 设置为 true 时,您还应在 host 属性中提供组播地址。

UDP 是一种高效但不可靠的协议。 Spring Integration 添加了两个属性以提高可靠性:check-lengthacknowledge。 当 check-length 设置为 true 时,适配器会在消息数据前添加一个长度字段(四个字节,采用网络字节序)。 这使得接收方能够验证接收到的数据包长度。 如果接收系统使用的缓冲区不足以容纳该数据包,数据包可能会被截断。 length 头提供了一个检测此情况的机制。spring-doc.cadn.net.cn

从版本 4.3 开始,您可以将 port 设置为 0,在这种情况下由操作系统选择端口。 适配器启动后,可以通过调用 getPort() 并在 isListening() 返回 true 时获取所选的端口。spring-doc.cadn.net.cn

从版本 5.3.3 开始,您可以添加一个 SocketCustomizer Bean 来在创建后修改 DatagramSocket(例如,调用 setTrafficClass(0x10))。spring-doc.cadn.net.cn

以下示例展示了一个出站通道适配器,该适配器为数据报包添加了长度检查:spring-doc.cadn.net.cn

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    check-length="true"
    channel="exampleChannel"/>
数据包接收方也必须配置为预期在数据之前存在长度信息。 对于 Spring Integration UDP 入站通道适配器,请设置其 check-length 属性。

第二次可靠性改进允许使用应用层确认协议。 接收方必须在指定时间内向发送方发送确认。spring-doc.cadn.net.cn

以下示例展示了一个出站通道适配器,它为数据报包添加长度检查并等待确认:spring-doc.cadn.net.cn

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    check-length="true"
    acknowledge="true"
    ack-host="thishost"
    ack-port="22222"
    ack-timeout="10000"
    channel="exampleChannel"/>
acknowledge 设置为 true 意味着数据包的接收方能够解释添加到包含确认数据(主机和端口)的数据包上的头部信息。 最可能的情况是,接收方是一个 Spring Integration 入站通道适配器。
当 multicast 为 true 时,一个额外的属性(min-acks-for-success)指定在 ack-timeout 内必须接收到多少个确认响应。

从 4.3 版本开始,您可以将 ackPort 设置为 0,在这种情况下,操作系统将选择端口。spring-doc.cadn.net.cn

出站 UDP 适配器(Java 配置)

以下示例展示了如何使用 Java 配置出站 UDP 适配器:spring-doc.cadn.net.cn

@Bean
@ServiceActivator(inputChannel = "udpOut")
public UnicastSendingMessageHandler handler() {
    return new UnicastSendingMessageHandler("localhost", 11111);
}

(或 MulticastSendingChannelAdapter 用于组播)。spring-doc.cadn.net.cn

出站 UDP 适配器(Java DSL 配置)

以下示例展示了如何使用 Java DSL 配置出站 UDP 适配器:spring-doc.cadn.net.cn

@Bean
public IntegrationFlow udpOutFlow() {
    return f -> f.handle(Udp.outboundAdapter("localhost", 1234)
                    .configureSocket(socket -> socket.setTrafficClass(0x10)))
                .get();
}

入站 UDP 适配器(XML 配置)

以下示例展示了如何配置基本的单播入站 UDP 通道适配器。spring-doc.cadn.net.cn

<int-ip:udp-inbound-channel-adapter id="udpReceiver"
    channel="udpOutChannel"
    port="11111"
    receive-buffer-size="500"
    multicast="false"
    socket-customizer="udpCustomizer"
    check-length="true"/>

下面的示例展示了如何配置一个基本的多播入站 UDP 通道适配器:spring-doc.cadn.net.cn

<int-ip:udp-inbound-channel-adapter id="udpReceiver"
    channel="udpOutChannel"
    port="11111"
    receive-buffer-size="500"
    multicast="true"
    multicast-address="225.6.7.8"
    check-length="true"/>

默认情况下,不会对入站数据包执行反向 DNS 查找:在 DNS 未配置的环境中(例如 Docker 容器),这可能会导致连接延迟。 若要将 IP 地址转换为用于消息头的主机名,可通过将 lookup-host 属性设置为 true 来覆盖默认行为。spring-doc.cadn.net.cn

从版本 5.3.3 开始,您可以添加一个 SocketCustomizer bean 以在创建后修改 DatagramSocket。 它用于接收套接字以及为发送确认而创建的任意套接字。spring-doc.cadn.net.cn

入站 UDP 适配器(Java 配置)

以下示例展示了如何使用 Java 配置入站 UDP 适配器:spring-doc.cadn.net.cn

@Bean
public UnicastReceivingChannelAdapter udpIn() {
    UnicastReceivingChannelAdapter adapter = new UnicastReceivingChannelAdapter(11111);
    adapter.setOutputChannelName("udpChannel");
    return adapter;
}

以下示例展示了如何使用 Java DSL 配置入站 UDP 适配器:spring-doc.cadn.net.cn

入站 UDP 适配器(Java DSL 配置)

@Bean
public IntegrationFlow udpIn() {
    return IntegrationFlow.from(Udp.inboundAdapter(11111))
            .channel("udpChannel")
            .get();
}

服务器监听事件

从版本 5.0.2 开始,当入站适配器启动并开始监听时,会发出一个UdpServerListeningEvent。 当适配器配置为在端口0上监听(即由操作系统选择端口)时,这非常有用。 如果您需要在启动其他将连接到该套接字的进程之前等待,也可以使用它来替代轮询isListening()spring-doc.cadn.net.cn

高级出站配置

The <int-ip:udp-outbound-channel-adapter> (UnicastSendingMessageHandler) 具有 destination-expressionsocket-expression 选项。spring-doc.cadn.net.cn

您可以使用 destination-expression 作为硬编码的 host-port 对的运行时替代方案,以确定发往 requestMessage(评估上下文的根对象)的传出数据报包的目标地址。 该表达式必须求值为一个 URI、URI 风格的 String(参见 RFC-2396),或一个 SocketAddress。 您也可以为此表达式使用入站 IpHeaders.PACKET_ADDRESS 头。 在框架中,当我们在 UnicastReceivingChannelAdapter 中接收数据报并将其转换为消息时,DatagramPacketMessageMapper 会填充此头。 该头的值正是入站数据报 DatagramPacket.getSocketAddress() 的结果。spring-doc.cadn.net.cn

使用 socket-expression,出站通道适配器可以使用(例如)入站通道适配器套接字,通过接收数据报的同一端口发送数据报。 这在我们的应用程序作为 UDP 服务器且客户端位于网络地址转换 (NAT) 之后的场景中非常有用。 此表达式必须求值为 DatagramSocketrequestMessage 用作求值上下文的根对象。 您不能将 socket-expression 参数与 multicastacknowledge 参数一起使用。 以下示例展示了如何配置一个 UDP 入站通道适配器,该适配器包含一个转换器,用于转换为大写并使用套接字:spring-doc.cadn.net.cn

<int-ip:udp-inbound-channel-adapter id="inbound" port="0" channel="in" />

<int:channel id="in" />

<int:transformer expression="new String(payload).toUpperCase()"
                       input-channel="in" output-channel="out"/>

<int:channel id="out" />

<int-ip:udp-outbound-channel-adapter id="outbound"
                        socket-expression="@inbound.socket"
                        destination-expression="headers['ip_packetAddress']"
                        channel="out" />

下面的示例展示了使用 Java DSL 的等效配置:spring-doc.cadn.net.cn

@Bean
public IntegrationFlow udpEchoUpcaseServer() {
    return IntegrationFlow.from(Udp.inboundAdapter(11111).id("udpIn"))
            .<byte[], String>transform(p -> new String(p).toUpperCase())
            .handle(Udp.outboundAdapter("headers['ip_packetAddress']")
                    .socketExpression("@udpIn.socket"))
            .get();
}