Post

Replies

Boosts

Views

Activity

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()
2
0
864
Dec ’23
Inconsistency in FD creation of NWConnection on server side when compared within system (using localhost) and between systems.
During my exploration of NWConnection and NWListener I created sample code to implement communication between client and listener over UDP. To achieve that client and listener were created on the same machine using localhost. Same client and listener codes were also deployed on different macOS machines. Following are the observations: Observation1: Within the same macOS machine, Listener was created on a port X and from client side I used "localhost" and Port X to connect to the listener. To capture the behavior of the how the connections/FDs are getting created I used lsof & netstat commands and following were the output: LSOF: tw-macoffice-02-studio:~ nikhil.singh$ lsof -Pi :9001 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME NWListner 16775 nikhil.singh 4u IPv6 0x402b8381d3a12129 0t0 UDP *:9001 NWListner 16775 nikhil.singh 5u IPv6 0x402b8381d3a11129 0t0 UDP localhost:9001->localhost:64723 NWConnect 16785 nikhil.singh 4u IPv4 0x402b8381d3a10529 0t0 UDP localhost:64723->localhost:9001 Here, we can see that separate FDs are getting created for: NWListner - Listener FD - 4u and NWConnection (connection request) from listener to client FD - 5u on localhost with Process ID 16775 NWConnect - NWConnection from client to listener on localhost FD - 4u Process ID 16785 NETSTAT: tw-macoffice-02-studio:~ nikhil.singh$ netstat -an |grep 9001 udp4 0 0 127.0.0.1.9001 127.0.0.1.64723 udp4 0 0 127.0.0.1.64723 127.0.0.1.9001 udp46 0 0 *.9001 . Connection wise also we can see the two connections are listed. Observation 2: I used different macOS machines within the same network to implement listener and client side of the code. Here, the behavior in terms of socket FD creation on the listener side with incoming connection request was different. Listener Side: LSOF: nikhil.singh@Mac-Pro ~ % lsof -Pi :9001 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME UDP_Serve 31480 nikhil.singh 5u IPv6 0xb3b7cb5ed6d2edd7 0t0 UDP *:9001 NETSTAT: nikhil.singh@Mac-Pro ~ % netstat -an |grep 9001 udp46 0 0 *.9001 . udp4 0 0 10.20.16.144.9001 10.20.16.250.62758 Inconsistency: **For listener side, a new socket FD was not created when listener accepted the incoming connection. ** Client Side: LSOF tw-macoffice-02-studio:~ nikhil.singh$ lsof -Pi :9001 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME NWConnect 29015 nikhil.singh 4u IPv4 0x402b8381d3c74129 0t0 UDP 10.20.16.250:62758->tw-macoffice-01.tallysolutions.com:9001 NETSTAT tw-macoffice-02-studio:~ nikhil.singh$ netstat -an |grep 9001 udp4 0 0 10.20.16.250.62758 10.20.16.144.9001 For Client Side, a new FD was created from client to listener. Question: Why a new socket FD was not created on the Listener side when incoming connection was from a different machine over IP. Also, one Digression question: As a cross platform application, we plan to use Network Framework for Apple Kernel and BSD sockets for others. Similar to BSD, is there a way we can block a thread on connection.receiveMessage?
1
0
391
Jan ’24