由连接到 IPAddress.Any 和 127.0.0.1 的两个客户端处理 UDP 数据包

Process UDP packet by two clients connected to IPAddress.Any and 127.0.0.1

我正在尝试将两个 UdpClient 连接到一个端口,以便它们并行读取数据并在两个单独的队列中处理请求。但由于某种原因,只有一个客户端收到数据包。

let listen (local:IPEndPoint) =
    async {
        use client = new UdpClient()
        client.ExclusiveAddressUse <- false
        client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)
        client.Client.Bind(local);

        printfn "Listen: %A" local.Address

        while true do
            let mutable remote = local
            client.Receive(&remote) |> ignore
            printfn "%A: %A" local.Address remote.Address
    }

Async.Parallel [
    listen(new IPEndPoint(IPAddress.Any, 5353));
    listen(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5353));
    async {
        while true do
            do! Async.Sleep 500

            use senderUC = new UdpClient()
            senderUC.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)
            senderUC.Send([|byte(1)|], 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5353)) |> ignore

            printfn "sent to 127.0.0.1"
    }
] |> Async.RunSynchronously |> ignore


Thread.Sleep 10000

输出如下:

Listen: 127.0.0.1
Listen: 0.0.0.0
sent to 127.0.0.1
127.0.0.1: 127.0.0.1
...
sent to 127.0.0.1
127.0.0.1: 127.0.0.1

删除其中一个客户端可以使另一个客户端很好地读取数据包。

有没有办法让他们都收到数据包?

不,一般来说,一旦数据包 'removed from the TCP/UDP stack' 就被视为已处理。第一个客户端在收到它时将其删除,从 OS 和框架的角度来看,它已被处理,不会被传递给更多的听众。

您必须分两步实施您的解决方案:1 个 UDP 侦听器接收数据并将其交给 2 个处理器并行处理。

But for some reason only one of the clients receives the packets.

嗯,原因是,每个 IP 堆栈都是这样设计的。

除了@Mau的建议,如果你的平台是Linux,iptables或许可以达到你想要的效果:

虽然两个答案都是正确的,但实际上可以使用 UDP 多播。然而,这需要您网络中的路由器真正支持多播。要使用多播,您需要在套接字上设置更多选项

//Creates a multicast publisher
let publisher multicastGroup ttl =
    let client = new UdpClient()
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastGroup, IPAddress.Any))
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, ttl)
    client

//Creates a multicast listener
let listener port multicastGroup =
    let client = new UdpClient()
    client.ExclusiveAddressUse <- false
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true) 
    client.Client.Bind(new IPEndPoint(IPAddress.Any, port))
    client.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastGroup, IPAddress.Any))
    client

然后您可以像在代码中那样使用发送和接收方法。有关在野外的示例,请参见 here