Choosing interface for multicast UDP

Hi all. Title says it all really. I have an app on MacOS that sends multicast UDP to multiple iOS devices. It shows realtime bar numbers of music played back by a playback software. It was all working until I tried a direct ethernet connection between Mac and phone - a scenario that would be quite common in the theatre world where machines dont have internet connection and people dont rely on wifi. How can I choose which interface my app sends the data through? Here is my connection function:

func openConnection() {
        let params = NWParameters.udp
        
        params.allowLocalEndpointReuse = true
        params.includePeerToPeer = true 

        let endpoint = NWEndpoint.hostPort(host: host, port: port)
        let conn = NWConnection(to: endpoint, using: params)
        self.connection = conn

        conn.stateUpdateHandler = { state in
            switch state {
            case .ready:
                print("Multicast connection ready to \(self.host)")
                UDPClient.console.log("Multicast connection ready to \(self.host)")
            case .failed(let error):
                print("Multicast connection failed: \(error)")
            default:
                break
            }
        }

        let udpQueue = DispatchQueue(label: "UDPClientQueue")
        conn.start(queue: udpQueue)
    }

Thanks for any help in advance!

Answered by DTS Engineer in 844390022
How can I choose which interface my app sends the data through?

With a normal UDP connection you can force the flow over a specific interface by setting the requiredInterface property on the NWParameters. However:

  • You’re using multicast, and NWConnection isn’t the right type to use for that. Rather, you should be using NWConnectionGroup with an NWMulticastGroup descriptor.

  • In theory that should also support requiredInterface, but I’ve had a lot of troubles getting that API to support things like this.

So, the above should give you some paths to explore. If you get stuck, reply here with the details and I’ll take another look.

ps I have a bunch of background info on this topic inBroadcasts and Multicasts, Hints and Tips.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Should have added here is where I set up my variables:

class UDPClient {
    static let shared = UDPClient()
    
    private var connection: NWConnection?
    private let host = NWEndpoint.Host("239.255.42.99")
    private let port: NWEndpoint.Port = 54321
    private var listener: NWListener?
    
    private let receiveQueue = DispatchQueue(label: "UDPReceiveQueue")
    private var activeConnections: [NWConnection] = []
How can I choose which interface my app sends the data through?

With a normal UDP connection you can force the flow over a specific interface by setting the requiredInterface property on the NWParameters. However:

  • You’re using multicast, and NWConnection isn’t the right type to use for that. Rather, you should be using NWConnectionGroup with an NWMulticastGroup descriptor.

  • In theory that should also support requiredInterface, but I’ve had a lot of troubles getting that API to support things like this.

So, the above should give you some paths to explore. If you get stuck, reply here with the details and I’ll take another look.

ps I have a bunch of background info on this topic inBroadcasts and Multicasts, Hints and Tips.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you. I have got it working with NWMulticastGroup but, interfacing aside, I have an issue I had back when I was using NWConnection as well. After a period of inactivity (5 minutes) the receiver is buffering all the messages it receives within a second and then releasing them all. My server is sending out 20 messages per second. Wireshark confirms that these are leaving as a steady stream, but the receiving side (IOS) becomes jittery and updates every second. If I close the group and reopen the connection it's solved, but then I cant send messages back to the MacOS app without also closing and reopening that connection. Here is the start of my listener on iOS:

func openConnection() {

        let host = NWEndpoint.Host(multicastAddress)
        let multicastEndpoint = NWEndpoint.hostPort(host: host, port: port)

        do {
            activity = ProcessInfo.processInfo.beginActivity(options: [.background, .latencyCritical], reason: "Multicast receiving")
            let group = try NWMulticastGroup(for: [multicastEndpoint])
            self.multicastGroup = group
            let parameters = NWParameters.udp
            parameters.allowLocalEndpointReuse = true
            parameters.includePeerToPeer = true
            parameters.serviceClass = .responsiveData
            
            // Bind to 0.0.0.0:54321
            let localHost = NWEndpoint.Host.ipv4(.any)
            let localPort = port
            parameters.requiredLocalEndpoint = .hostPort(host: localHost, port: localPort)

            // Then create the group
            let connectionGroup = NWConnectionGroup(with: group, using: parameters)
            self.connectionGroup = connectionGroup
            
            connectionGroup.setReceiveHandler(maximumMessageSize: 65536, rejectOversizedMessages: true) { data, content, isComplete in
    
                guard let data = content, data.count >= 4 else {
                    print("No or insufficient multicast data")
                    return
                }

As you can see I have tried parameters.serviceClass = .responsiveData and also activity = ProcessInfo.processInfo.beginActivity(options: [.background, .latencyCritical], reason: "Multicast receiving")

thanks in advance!

Accepted Answer

For anyone following this I think my buferent and delayed packets were to do with multicast packets going through my router rather than anything in code. I have moved to bonjour and then multiple unicast TCP connections which is working well. Required interface also works in this method.

Choosing interface for multicast UDP
 
 
Q