Post

Replies

Boosts

Views

Activity

Reply to Using two network protocol framer in the protocols stack
Thanks for your reply. The server receives nothing, none of the protocol has its handleInput() method called. Here are the logs: # Client Protocol1 start() Protocol2 start() sending 2990 bytes Protocol2 handleOutput() # Server Protocol1 start() Protocol2 start() Protocol1 is placed at index 1, Protocol2 is on top at index 0. Instead I would expect this to happen: # Client Protocol1 start() Protocol2 start() sending 2990 bytes Protocol2 handleOutput() Protocol1 handleOutput() # Server Protocol1 start() Protocol2 start() Protocol1 handleInput() Protocol2 handleInput() received 2990 bytes I insert the protocols on the NWListener (server-side) and the NWConnection (client-side) like so: let protocol1 = NWProtocolFramer.Options(definition: Protocol1.definition) let protocol2 = NWProtocolFramer.Options(definition: Protocol2.definition) let parameters: NWParameters = .tcp parameters.defaultProtocolStack.applicationProtocols.insert(protocol1, at: 0) parameters.defaultProtocolStack.applicationProtocols.insert(protocol2, at: 0)
Jul ’21
Reply to Using two network protocol framer in the protocols stack
If that helps, my handleOuptut() method looks like this: func handleOutput(framer: NWProtocolFramer.Instance, message: NWProtocolFramer.Message, messageLength: Int, isComplete: Bool) { print("Protocol2 handleOutput()") var length = Int32(messageLength) let data = Data(bytes: &length, count: MemoryLayout<Int32>.size) framer.writeOutput(data: data) try? framer.writeOutputNoCopy(length: messageLength) }
Jul ’21
Reply to Exchange information with a network protocol framer
If we could pass some context into NWProtocolFramer.Options and access it from the protocol framer, that would also solve my problem. Actually it wouldn't solve my problem, for the listener the options are inserted into NWListener and not NWConnection. Rather I need access to the connection itself from my protocol framer, or to storage/context associated with the connection. How do people solve that problem? It seems to me this is a pretty basic need but it is seemingly impossible to do, am I missing something here?
Jul ’21
Reply to Network protocol framer and receiving partial messages
Further testing indicates the latter: data is not dropped but the framer fails to receive more data, and even though handleInput() is called again, the buffer does not contain more to parse. Consuming the available (partial) data seems to unblock the situation as the framer is then able to receive the missing piece of the data. Is this the correct behavior? Am I trying to parse too much data at once? If so, what is the upper limit we can parse without possibly entering this deadlock situation?
Jul ’21
Reply to Network protocol framer and receiving partial messages
I do have a length value in the packet header but I can't call deliverInputNoCopy because my protocol needs to look at all the data (doing custom encryption/decryption, not TLS on purpose). Therefore I am trying to parse the whole length read in the header. I have seen nothing in the documentation or wwdc sessions about the framer.parseInput() API potentially starving depending on the minimum amount of data requested. Would you be so kind to at least give us the range of values we can expect to pass safely to the API?
Jul ’21
Reply to Exchange information with a network protocol framer
Thank you for this reply. It doesn't seem like the TLS security options is what I need since I am implementing my own protocol which needs to pass its own custom options (unless I try to abuse these security options by inserting a pointer into some dispatch data object that could be stashed somewhere in there). But it does seem like what I want is NWParameters but for my protocol, alas the Network API does not seem to allow this.
Jul ’21
Reply to Exchange information with a network protocol framer
Right, I wouldn't even dare to write a custom TLS implementation. I am implementing custom encryption that will only work with my application on both ends, it's not meant to interoperate with other things. I thought I might be able to subclass NWParameters to pass custom parameters to my protocol but alas Swift prevents it: although the class is not marked final, it is also not marked open and therefore subclassing is prevented outside of the Network module.
Jul ’21
Reply to NWConnection send buffer and when to send more data
It does reach the other side. What I'm saying is that I can send data onto the connection much more quickly than it actually goes out on the network, which causes it to enqueue in memory (a lot). I was expecting the connection to have a small buffer and only call the completion handler of the send() method when enough data had left the buffer and went onto the network. In the current state of things, how would I know how fast the data is coming out of this internal buffer and onto the network? I would certainly need some sort of back pressure information in order to know whether it is ok to give more data to the connection. Or am I missing something?
Aug ’21
Reply to NWConnection send buffer and when to send more data
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)
Sep ’21
Reply to NWConnection send buffer and when to send more data
Using a method like this to handle input is roughly the same as calling connection.receive(minimumIncompleteLength: 1, maximumLength: 1000), is there a reason you are not parsing out the length of your frame from the packet header? Hi Matt, in my app I do parse the length from the packet header. This is a reduced sample code, the simplest possible that demonstrates the problem. I added a protocol framer that basically does nothing (just passing the data along), and it changed the behavior of the writer, which seems wrong to me. About your code, you need to remove these three lines: if error == nil { startReceive(on: connection) } Because the whole point is to see what happens when the other side does not read (or rather does not read fast enough). If you remove these three lines, you will see that the sender keeps enqueuing indefinitely into memory explosion.
Sep ’21