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()