Ok Quinn. I made further testing and the problem seems related to me using a protocol framer. Give it a try and let me know what you think.
import Foundation
import Network
class MyProtocol: NWProtocolFramerImplementation {
// Create a global definition of your game protocol to add to connections.
static let definition = NWProtocolFramer.Definition(implementation: MyProtocol.self)
// Set a name for your protocol for use in debugging.
static var label: String { return "MyProtocol" }
// Set the default behavior for most framing protocol functions.
required init(framer: NWProtocolFramer.Instance) { }
func start(framer: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult {
return .ready
}
func wakeup(framer: NWProtocolFramer.Instance) { }
func stop(framer: NWProtocolFramer.Instance) -> Bool { return true }
func cleanup(framer: NWProtocolFramer.Instance) { }
// Whenever the application sends a message, add your protocol header and forward the bytes.
func handleOutput(framer: NWProtocolFramer.Instance, message: NWProtocolFramer.Message, messageLength: Int, isComplete: Bool) {
try? framer.writeOutputNoCopy(length: messageLength)
}
// Whenever new bytes are available to read, try to parse out your message format.
func handleInput(framer: NWProtocolFramer.Instance) -> Int {
let message = NWProtocolFramer.Message(definition: MyProtocol.definition)
_ = framer.deliverInputNoCopy(length: 1000, message: message, isComplete: true)
return 0
}
}
var listenerRef: NWListener? = nil
var receiveConnectionRef: NWConnection? = nil
func startListener() {
let options = NWProtocolFramer.Options(definition: MyProtocol.definition)
let parameters = NWParameters.tcp
parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
let listener = try! NWListener(using: parameters, on: 12345)
listenerRef = listener
listener.stateUpdateHandler = { state in
print("listener: state did change, new: \(state)")
}
listener.newConnectionHandler = { conn in
if let old = receiveConnectionRef {
print("listener: will cancel old connection")
old.cancel()
receiveConnectionRef = nil
}
receiveConnectionRef = conn
startReceive(on: conn)
conn.start(queue: .main)
}
listener.start(queue: .main)
}
func startReceive(on connection: NWConnection) {
connection.receiveMessage { dataQ, _, isComplete, errorQ in
if let data = dataQ {
print("receiver: did received, count: \(data.count)")
}
if let error = errorQ {
print("receiver: did fail, error: \(error)")
return
}
// if isComplete {
// print("receiver: is complete")
// return
// }
print("receiver: will not start new receive to force back pressure")
}
}
var sendConnectionRef: NWConnection? = nil
var totalSent = 0
func sendRandomData(to connection: NWConnection) {
var bytes = [UInt8](repeating: 0, count: 1000)
let err = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
assert(err == errSecSuccess)
let message = NWProtocolFramer.Message(definition: MyProtocol.definition)
let context = NWConnection.ContentContext(identifier: "Data", metadata: [message])
connection.send(content: Data(bytes), contentContext: context, completion: .contentProcessed({ errorQ in
if let error = errorQ {
print("sender: send failed, error: \(error)")
return
}
totalSent += bytes.count
print("sender: did send, total: \(totalSent)")
sendRandomData(to: connection)
}))
}
func startSender() {
let options = NWProtocolFramer.Options(definition: MyProtocol.definition)
let parameters = NWParameters.tcp
parameters.defaultProtocolStack.applicationProtocols.insert(options, at: 0)
let connection = NWConnection(host: "localhost", port: 12345, using: parameters)
sendConnectionRef = connection // Guarantees a long-lived referenced.
connection.stateUpdateHandler = { state in
print("sender: state did change, new: \(state)")
}
sendRandomData(to: connection)
connection.start(queue: .main)
}
func main() {
startListener()
startSender()
dispatchMain()
}
main()
exit(EXIT_SUCCESS)