Using NWConnectionGroup over UDP for implementing multicast group over multicast IP & given port range.

Our App design requires Apps to be able to discover and communicate with other Apps in the same Local/Enterprise network over a range of given ports. To facilitate this, I am exploring NWConnectionGroup over UDP.

As per the following documentation (How to use multicast networking in your app) I am writing sample code where objective is to create a Multicast group among two different programs running on the same macOS (14.1) system and enabling multicast communication between them.

Based on the following note in documentation I understand that I don’t need multicast entitlement if I am using sample code (Command Line Tool).

Note: You can test your app using the iOS and iPadOS simulators without an active entitlement, but using multicast and broadcast networking on physical hardware requires the entitlement.

Is this understanding correct? If yes.

Approach 1:

When I create multicast group in both the programs on same multicast IP and Port (224.0.0.251:8900), the second program is not executing and gives following "Address already in use" error. I have used allowLocalEndpointReuse (true) while creating NWConnectionGroup. Is this expected?

Error Logs:

Group entered state failed(POSIXErrorCode(rawValue: 48): Address already in use) Send complete with error Optional(POSIXErrorCode(rawValue: 89): Operation canceled) nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error 48 nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed nw_listener_start_on_queue [L1] nw_path_create_evaluator_for_listener failed nw_connection_group_handle_listener_state_change [G1] listener failed with error nw_connection_group_send_message_internal [G1] Cannot send a message on a failed or cancelled group

As per attached sample code, do you see anything specific which I might be missing, pointers to that will help.

Note: Difference between Approach1 and Approach2 is only the Port Number

Second Approach:

So, I tried with same multicast IP but binding to different ports for both the programs (224.0.0.251:8900 & 224.0.0.251:8901). Here, for both the programs group.send() to self was getting received by the group.setReceiveHandler() but same multicast message was not being received by the other program.

Question: Is this approach correct? If yes, what could be the reason behind NOT receiving the multicast messages?

/************** NWConnectionGroup 1*********************/
import Foundation
import Network

// Create a NWMulticastGroup to describe the multicast group
guard let multicast = try? NWMulticastGroup(for: [.hostPort(host: "224.0.0.250", port: 8900)]) else {
    fatalError("Error creating multicast group")
}

let params = NWParameters.udp;
params.allowLocalEndpointReuse = true;

// Create an NWConnectionGroup to handle multicast communication
let group = NWConnectionGroup(with: multicast, using: params)

// Set the handler for incoming messages
group.setReceiveHandler(maximumMessageSize: 16384, rejectOversizedMessages: true) { (message, content, isComplete) in
    
    print("Received message from \(String(describing: message.remoteEndpoint))")
    
    if let content = content, let message = String(data: content, encoding: .utf8) {
            print("Received Message: \(message)")}
    
    //print("Current group members:", group.descriptor.members)
    for member in group.descriptor.members {
        switch member {
        case .hostPort(let host, let port):
            print("Member Host: \(host), Port: \(port)")
        default:
            print("Member Endpoint: \(member)")
        }
    }
    // Send acknowledgment
    //let sendContent = Data("ack from Device 1".utf8)
    /*group.send(content: sendContent) { (error) in
        print("Acknowledgment sent with error: \(String(describing: error))")
    }*/
    if isComplete {
        print("Connection closed")
    }
}

// Set the state update handler
group.stateUpdateHandler = { (newState) in
    print("Group entered state \(String(describing: newState))")
}

// Start the NWConnectionGroup
group.start(queue: .main)

// Send a message to the multicast group
let groupSendContent = Data("Hello All from Device 1".utf8)
group.send(content: groupSendContent) { (error) in
    print("Send complete with error \(String(describing: error))")
}

// Keep the run loop running
RunLoop.main.run()
/***************************NWConnectionGroup 2 **********************/
import Foundation
import Network

// Create a NWMulticastGroup to describe the multicast group
guard let multicast = try? NWMulticastGroup(for: [.hostPort(host: "224.0.0.250", port: 8901)]) else {
    fatalError("Error creating multicast group")
}

let params = NWParameters.udp;
params.allowLocalEndpointReuse = true;

// Create an NWConnectionGroup to handle multicast communication
let group = NWConnectionGroup(with: multicast, using: params)

// Set the handler for incoming messages
group.setReceiveHandler(maximumMessageSize: 16384, rejectOversizedMessages: true) { (message, content, isComplete) in
    
    print("Received message from \(String(describing: message.remoteEndpoint))")

    if let content = content, let message = String(data: content, encoding: .utf8) {
            print("Received Message: \(message)")}

    // Send acknowledgment
    /*let sendContent = Data("ACK from Device 2".utf8)
    group.send(content: sendContent, to: message.remoteEndpoint) { (error) in
        print("Acknowledgment sent with error: \(String(describing: error))")
    }*/
    if isComplete {
        print("Connection closed")
    }
}

// Set the state update handler
group.stateUpdateHandler = { (newState) in
    print("Group entered state \(String(describing: newState))")
}

// Start the NWConnectionGroup
group.start(queue: .main)

// Send a message to the multicast group
let groupSendContent = Data("Hello All from Device 2".utf8)
group.send(content: groupSendContent) { (error) in
    print("Send complete with error \(String(describing: error))")
}

// Keep the run loop running
RunLoop.main.run()

Replies

Forgot to ask

Based on above observations:

  • Does this mean that group members are supposed to be on the same Multicast IP Address and Port.
  • Process on the same device cannot have same IP address and Port.

Inferring: Will this only work when processes in a group are on different devices?

Similarly, is there a way to use same multicast group for all endpoints who are bound on any port from a given port range and have joined the same multicast group address?

For example:

  • 'Process A' bound on 'PortX' has joined a multicast group address IP-A

  • 'Process B' binded on 'PortX' and joined the multicast group address IP-A

  • 'Process C' binded on 'PortY' and joined multicast group address IP-A

Can they all form one group where say PortX and PortY are any port from a range of say 18001 to 18100?

Note: We have done explorations where this was possible using BSD sockets by sending multicast to a group address and all ports in a given range.

Answers to these will be helpful.

This came up in another context and I wanted to close the loop here.

When I create multicast group in both the programs on same multicast IP and Port (224.0.0.251:8900), the second program is not executing and gives following "Address already in use" error.

This is a known bug in Network framework (r. 105227856). If some other program has bound to the same port, NWConnectionGroup will fail to start up with the EADDRINUSE (48) error. This is true regardless of whether the first program is using BSD Sockets or Network framework for its multicast.

If you really need to have multiple apps on the same device listen for multicasts on the same port, your best option right now is to use BSD Sockets )-:

Share and Enjoy

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

  • Hi @eskimo,

    QUOTE If you really need to have multiple apps on the same device listen for multicasts on the same port, your best option right now is to use BSD Sockets )-: UNQOUTE

    Will BSD socket approach work on all Apple OSes - macOS, iOS, iPadOS, tvOS, watchOS?

    QOUTE This is a known bug in Network framework (r. 105227856). UNQOUTE

    How can we track bugs? and check their status?

Add a Comment