I'm trying to determine when a UDP client stops sending packets to a server when using Network.framework
I've built out a small example which demonstrates the server failing to change states to .cancelled
when a client's connection is cancelled.
Example Client:
import Foundation
import Network
func sendMessage(on connection: NWConnection) {
connection.send(content: "hello".data(using: .utf8), completion: .contentProcessed({error in
if let error = error {
print("error while sending hello: \(error)")
return
}
connection.receiveMessage {data, context, isComplete, error in
if let error = error {
print("error while receiving reply: \(error)")
return
}
connection.cancel()
}
}))
}
var connection: NWConnection = {
let connection = NWConnection(
to: .service(
name: "Hello",
type: "_test._udp",
domain: "local",
interface: nil
),
using: .udp
)
connection.stateUpdateHandler = {newState in
switch newState {
case .ready:
sendMessage(on: connection)
case .failed(let error):
print("client failed with error: \(error)")
case .cancelled:
print("Cancelled connection")
default:
break
}
}
return connection
}()
connection.start(queue: DispatchQueue(label: "test"))
RunLoop.main.run()
Example Server:
import Foundation
import Network
func receive(on connection: NWConnection) {
connection.receiveMessage { (data, context, isComplete, error) in
if let error = error {
print(error)
return
}
connection.send(content: "world".data(using: .utf8), completion: .contentProcessed({error in
if let error = error {
print("error while sending data: \(error)")
return
}
}))
receive(on: connection)
}
}
var listener: NWListener = {
let listener = try! NWListener(using: .udp)
listener.service = NWListener.Service(name: "Hello", type: "_test._udp", domain: nil, txtRecord: nil)
listener.newConnectionHandler = {newConnection in
newConnection.stateUpdateHandler = {newState in
switch newState {
case .ready:
receive(on: newConnection)
case .failed(let error):
print("client failed with error: \(error)")
case .cancelled:
print("Cancelled connection")
default:
break
}
}
newConnection.start(queue: DispatchQueue(label: "new client"))
}
return listener
}()
listener.start(queue: DispatchQueue(label: "test"))
RunLoop.main.run()
When running the client while the server is running, the client sends and receives one packet and then is cancelled. The client prints Connection cancelled
. However, the state of the NWConnection on the server does not change, and connection.receiveMessage
fails silently when no data to read from the client.
I would expect either the server connection's state to change or receiveMessage
to call its completion handler despite no data being present (data
is Data?
after all)
So, I'm unsure how to detect when a client stops sending packets when using a UDP server on Network.framework. How should I go about detecting "disconnected" clients?
A UDP server gets no information via the network about whether a UDP client has disconnected or gone away, unless perhaps the client explicitly sends a some sort of additional message (via UDP, TCP, or other side channel) regarding its disconnect status. So there's nothing to change the NWConnection state (except perhaps some sort of problem with the server itself).
Perhaps the server can assume a disconnect after some agreed upon or negotiated timeout time has passed without some sort of activity. Or number of packets, bytes of data. Etc. And close the connection itself.