UDP 适配器

本節說明如何設定和使用 UDP 适配器。

輸出 UDP 适配器 (XML 設定)

以下範例設定 UDP 輸出通道适配器

<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 標頭提供了一種偵測此情況的機制。

從 4.3 版開始,您可以將 port 設定為 0,在這種情況下,作業系統會選擇埠。在适配器啟動且 isListening() 傳回 true 後,可以透過調用 getPort() 來發現所選的埠。

從 5.3.3 版開始,您可以新增 SocketCustomizer bean 以在建立 DatagramSocket 後修改它(例如,調用 setTrafficClass(0x10))。

以下範例顯示一個輸出通道适配器,它將長度檢查新增至資料包

<int-ip:udp-outbound-channel-adapter id="udpOut"
    host="somehost"
    port="11111"
    multicast="false"
    check-length="true"
    channel="exampleChannel"/>
封包的接收者也必須設定為預期長度位於實際資料之前。對於 Spring Integration UDP 輸入通道适配器,請設定其 check-length 屬性。

第二個可靠性改進允許使用應用程式層級的確認協定。接收者必須在指定時間內向發送者發送確認。

以下範例顯示一個輸出通道适配器,它將長度檢查新增至資料包並等待確認

<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,在這種情況下,作業系統會選擇埠。

輸出 UDP 适配器 (Java 設定)

以下範例顯示如何使用 Java 設定輸出 UDP 适配器

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

(或多播的 MulticastSendingChannelAdapter)。

輸出 UDP 适配器 (Java DSL 設定)

以下範例顯示如何使用 Java DSL 設定輸出 UDP 适配器

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

輸入 UDP 适配器 (XML 設定)

以下範例顯示如何設定基本單播輸入 udp 通道适配器。

<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 通道适配器

<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 來覆寫預設行為。

從 5.3.3 版開始,您可以新增 SocketCustomizer bean 以在建立 DatagramSocket 後修改它。它會針對接收套接字以及為發送 ack 建立的任何套接字調用。

輸入 UDP 适配器 (Java 設定)

以下範例顯示如何使用 Java 設定輸入 UDP 适配器

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

以下範例顯示如何使用 Java DSL 設定輸入 UDP 适配器

輸入 UDP 适配器 (Java DSL 設定)

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

伺服器監聽事件

從 5.0.2 版開始,當輸入适配器啟動並開始監聽時,會發出 UdpServerListeningEvent。當适配器設定為監聽埠 0 時,這非常有用,這表示作業系統會選擇埠。如果您需要在啟動將連線到套接字的其他程序之前等待,也可以使用它來代替輪詢 isListening()

進階輸出設定

<int-ip:udp-outbound-channel-adapter> (UnicastSendingMessageHandler) 具有 destination-expressionsocket-expression 選項。

您可以使用 destination-expression 作為硬編碼 host-port 對的執行階段替代方案,以針對 requestMessage(使用評估內容的根物件)判斷輸出資料包的目的地位址。運算式必須評估為 URI、URI 樣式的 String(請參閱 RFC-2396)或 SocketAddress。您也可以針對此運算式使用輸入 IpHeaders.PACKET_ADDRESS 標頭。在框架中,當我們在 UnicastReceivingChannelAdapter 中接收資料包並將其轉換為訊息時,DatagramPacketMessageMapper 會填入此標頭。標頭值正是傳入資料包的 DatagramPacket.getSocketAddress() 的結果。

使用 socket-expression,輸出通道适配器可以使用(例如)輸入通道适配器套接字,透過接收資料包的相同埠發送資料包。這在我們的應用程式作為 UDP 伺服器且用戶端在網路位址轉換 (NAT) 後方運作的情況下很有用。此運算式必須評估為 DatagramSocketrequestMessage 用作評估內容的根物件。您無法將 socket-expression 參數與 multicastacknowledge 參數一起使用。以下範例顯示如何設定具有轉換器的 UDP 輸入通道适配器,該轉換器轉換為大寫並使用套接字

<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 的等效設定

@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();
}