I’m building an app with this goal: I have a continuous stream of UDP datagrams coming in but NWListener stops listening as soon as it generates a NWConnection and doesn’t start listening again until the connection was cancelled and the listener restarted, which is all extremely time consuming and causes about 1/3 of the datagrams to be dropped. So I either find a solution through NWUDPSession or else there’s a trick to keep NWListener actively listening at all times in which case I’m all ears.
Post
Replies
Boosts
Views
Activity
Here’s the code that drops a huge amount of datagrams. I have also tried with queue .main with the same results. Using NWConnection.receive or .receiveMessage makes no difference.
import Network
import SwiftUI
class udpCatcher: ObservableObject {
init() {
print("init started.")
var lastTime = Date() // used to know how much time it took between receiving datagrams, mostly for my use in combination with optional filtering for specific data that can be enabled by un-commenting some code below
var counter = 0 // used to add indentation to better follow the scrolling of the data
let printState = false // if false then only the data will print, if true then also the state of the listener and the connection
let listener = try! NWListener(using: .udp, on: 5000)
listener.newConnectionLimit = NWListener.InfiniteConnectionLimit
listener.stateUpdateHandler = { state in
if printState {print("listener state: \(listener.state)")}
}
listener.newConnectionHandler = { connection in
if printState {print("newConnectionHandler triggered: connection state: \(connection.state), listener state: \(listener.state)")}
connection.stateUpdateHandler = { newState in
switch newState {
case .ready:
if printState {print("Connection state: \(connection.state), listener state: \(listener.state)")}
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { (data, context, isComplete, error) in
if let data = data, !data.isEmpty {
var message = String(decoding: data, as: UTF8.self)
// if message.hasPrefix("$XXXX") { // for optional filtering of messages
counter += 1
if counter % 5 == 0 { // indentation every 5th line to better follow the scrolling
print("\(counter) Received data: \(message) \((Date().timeIntervalSince(lastTime)*100).rounded()/100)\"")
} else {
print("\(counter) Received data: \(message) \((Date().timeIntervalSince(lastTime)*100).rounded()/100)\"")
}
lastTime = Date()
// }
// Process the data here
}
if let error = error {
if printState {print("Connection error: \(error)")}
}
if isComplete {
connection.cancel()
if printState {print("Connection state: \(connection.state), listener state: \(listener.state)")}
}
}
case let .failed(error):
if printState {print("Connection failed with error \(error.localizedDescription), listener state: \(listener.state)")}
default:
if printState {print("Connection state: \(connection.state), listener state: \(listener.state)")}
}
}
connection.start(queue: .global(qos: .userInteractive))
}
listener.start(queue: .global(qos: .userInteractive))
}
}
All the adresses and ports are identical between datagrams.
Regarding BSD, could you please help me on my way? This isn’t built into Playgrounds and not part of its documentation either. Can you give me links to files, documentation, and code snippets and can you tell me how to import it into Playgrounds on iPad? That would save me hours, if not days, and would be mightily appreciated! Thanks.
It’s broadcast.
I’d love to stick with Network but as far as I can tell as a beginning Swift developer, it’s broken. newConnectionHandler won’t be triggered until the current connection is closed therefore dropping all the datagrams in the interval. Unless you have a fix for that through threads or whatever, there’s going to be no way around it. I’ve already tried different threads in the queues of the listener and connection to no avail. I haven’t found any working sample code on the internet. I’m stumped.
What I did find is complaints about datagrams being dropped going back 3 years, with no solution offered.
But realistically, I expect I’m going to have to go the BSD route, or find yet another alternative. I did discover Darwin in the mean time and managed to start a UDP connection and receive the first few bytes. But it’s a hard slog without guidance. Trying infinite combinations of possible syntax in the hope of making it work is not fun. I really do need sample code if you can spare some. Is your BSD solution the same as going through Darwin or is it another alley?
I think I’m well enough on my way now to succeed with Darwin Sockets. I’m still curious to know whether that’s what you meant with BSD.Sockets or whether they are a third alternative. Anyway, if you don’t hear from me anymore it means I made it. Thank you Quinn for chiming in on this.