OK. Then let’s use that info to extend the steps in my previous post:
-
On the server, you create a set of parameters using NWParameters.tcp
.
-
And set allowLocalEndpointReuse
.
-
You then create and start an NWListener
for a specific port.
-
On the client, you open a TCP connection to that server.
-
On the server, the listener calls your new connection callback with an NWConnection
.
-
You exchange data on that connection.
-
You stop the server by calling cancel()
on all of its connections and then on the listener.
-
You repeat steps 1 through 3.
What happens then?
When I tried this with the test code below, I see this sequence:
listener did change state, new: ready
listener will start connection
listener did start connection
connection did change state, new: preparing
connection did change state, new: ready
connection did receive, count: 6
listener will stop
listener did stop
connection did receive, EOF
listener did change state, new: failed(POSIXErrorCode(rawValue: 48): Address already in use)
connection did fail, error: POSIXErrorCode(rawValue: 89): Operation canceled
I believe I’m seeing this because, due to the async nature of Network framework, the connection hasn’t finished shutting down at the point that I try to restart my listener. Notably, if I uncomment the lines that delay the listener start by a second, the problem goes away.
Is that an accurate reflection of your issue?
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
import Foundation
import Network
class Server {
var listenerQ: NWListener? = nil
var connections: [NWConnection] = []
func start() {
let params = NWParameters.tcp
params.allowLocalEndpointReuse = true
let listener = try! NWListener(using: params, on: 12345)
listener.stateUpdateHandler = { s in
print("listener did change state, new: \(s)")
}
listener.newConnectionHandler = { connection in
self.didAcceptConnection(connection)
}
listener.start(queue: .main)
self.listenerQ = listener
}
func stop() {
print("listener will stop")
for connection in self.connections {
connection.stateUpdateHandler = nil
connection.cancel()
}
connections.removeAll()
if let listener = self.listenerQ {
listener.stateUpdateHandler = nil
listener.newConnectionHandler = nil
listener.cancel()
self.listenerQ = nil
}
print("listener did stop")
}
func didAcceptConnection(_ connection: NWConnection) {
print("listener will start connection")
connection.stateUpdateHandler = { s in
print("connection did change state, new: \(s)")
}
self.startReceive(on: connection)
connection.start(queue: .main)
self.connections.append(connection)
print("listener did start connection")
}
func startReceive(on connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 2048) { content, _, isComplete, error in
if let content {
print("connection did receive, count: \(content.count)")
}
if isComplete {
print("connection did receive, EOF")
}
if let error {
print("connection did fail, error: \(error)")
return
}
self.startReceive(on: connection)
}
}
}
func main() {
let server = Server()
server.start()
DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
server.stop()
// DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
server.start()
// }
}
withExtendedLifetime(server) {
dispatchMain()
}
}
main()