Post

Replies

Boosts

Views

Activity

Reply to Using NWConnectionGroup over UDP for implementing multicast group over multicast IP & given port range.
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.
Dec ’23
Reply to Implementing Client and Server over UDP based custom protocol using Network Framework
Yes, this should be possible, you'll want to set allowLocalEndpointReuse to true in the NWParameters for both the connections and the listener. You'll then use the requiredLocalEndpoint to ask for a specific port. Note that it's often better to use the system-assigned port from your listener, rather than guessing at what one might be available, as that is prone to race conditions and is otherwise often tricky to get right. You'll see failures and log messages about addresses being already in use if it's not quite right. As per suggestion above, I have written sample code(below) to reuse a local port (9000) for both listener and connection. Here I am creating a UDP NWListener on port 9000 and then I created a NWConnection to connect this server as a client to another UDP Listener server (on port 9001). For both listener and connection I have set 'allowLocalEndpointReuse' as true and used requiredLocalEndpoint for connection. However, this is not working with following error logs: Server started on INADDR_ANY:9000 Client Connection preparing Client Connection waiting nw_socket_connect [C1:1] connectx(5 (guarded), [srcif=1, srcaddr=, dstaddr=], SAE_ASSOCID_ANY, 0, NULL, 0, NULL, SAE_CONNID_ANY) failed: 48 nw_socket_connect [C1:1] connectx failed (fd 5) 48 nw_socket_connect connectx failed 48 To ensure that there are no other process using the same port I have checked the lsof command for port 9000. It is showing UDP listner server only: nikhil.singh@tw-macoffice-01 ~ % lsof -i :9000 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME UDP_Serve 41362 nikhil.singh 4u IPv6 0x6febcdc1906bf539 0t0 UDP *:cslistener Please provide inputs if this is the correct way to use the "allowLocalEndpointReuse" and "requiredLocalEndpoint" as per the context you provided. If not, how can this code be corrected to reuse local port for listner and connections. As per our application design we need to use same local port for communication. Note: This code works if I change the local port for NWConnection to something different than 9000, for ex: 9002 import Foundation import Network // Create a UDP listener - set the listener's address to INADDR_ANY and the specified port let listener = try! NWListener(using: .udp, on: 9000) // Allow Local Endpoint Resue listener.parameters.allowLocalEndpointReuse = true // Handle new connections listener.newConnectionHandler = { connection in print("New connection request received") //Adding state update handler to monitor received connection request connection.stateUpdateHandler = { state in switch state { case .ready: print("Received connection request is ready") case .waiting: print("Received connection waiting") case .preparing: print("Received connection preparing") case .cancelled: print("Received connection cancelled") case .failed(let error): print("Received connection failed: \(error)") default: break } } //Starting the connection connection.start(queue: DispatchQueue.global()) // Start receiving data from the connection connection.receive(minimumIncompleteLength: 1, maximumLength: 1024) { data, context, isComplete, error in if let error = error { print("Error receiving data: \(error)") return } if let data = data { // Process received data print("Received data from client: \(String(data: data, encoding: .utf8)!)") // Optionally, send a response to the client let response = "Hello, client!" connection.send(content: response.data(using: .utf8)!, completion: .contentProcessed({ (error) in if let err = error { // Handle error in sending print("Error sending response: \(err)") } else { // Send has been processed print("Response sent successfully") } })) } if isComplete { print("Connection closed") } } } // Start the listener listener.start(queue: DispatchQueue.global()) print("Server started on INADDR_ANY:\(9000)") //**************Starting NWConnection from Server1 as a client ***************// // Allowing local port reuse for port 9000 let port = NWEndpoint.Port(rawValue: 9000) let localEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host("127.0.0.1"), port: port!) let parameters = NWParameters.udp parameters.requiredLocalEndpoint = localEndpoint // Starting connection from server to a different server as a client using NWConnection let cli_connection = NWConnection(host: NWEndpoint.Host( "127.0.0.1"), port: NWEndpoint.Port(rawValue: 9001)!, using: parameters) //Allowing Local Endpoint Reuse cli_connection.parameters.allowLocalEndpointReuse = true // Set up state update handler cli_connection.stateUpdateHandler = { state in switch state { case .ready: print("Client Connection established") // Send data to the server let message = "Hello, Server2!" cli_connection.send(content: message.data(using: .utf8)!, isComplete: true, completion: .contentProcessed({ (error) in if let err = error { // Handle error in sending print("Error sending response: \(err)") } else { // Send has been processed print("Request sent successfully") } })) case .waiting: print("Client Connection waiting") case .preparing: print("Client Connection preparing") case .cancelled: print("Client Connection cancelled") case .failed(let error): print("Client Connection failed: \(error)") default: break } } // Start the connection cli_connection.start(queue: DispatchQueue.global()) // Receive data from the server cli_connection.receive(minimumIncompleteLength: 1, maximumLength: 1024) { data, context, isComplete, error in if let error = error { print("Error receiving data: \(error)") } else if let data = data { print("Received 1 data from server: \(String(data: data, encoding: .utf8)!)") } if isComplete { print("Connection closed") } } // Keep the server running RunLoop.main.run()
Dec ’23