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.
Post
Replies
Boosts
Views
Activity
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()