NWProtocolFramer: Simple delimiter?

I'm experimenting with NWProtocolFramer / NWProtocolFramerImplementation and have a basic question. If I want to separate messages via a common delimeter, is the correct strategy to have handleInput() return 0 until the delimiter is encountered? Or is there something behind the scenes that makes this even easier? I've been looking at header files and nothing stands out as a shortcut to achieving this -- but I may be missing something!

Replies

is the correct strategy to have

handleInput(…)
return 0 until the delimiter is encountered?

Kinda. In

handleInput(…)
, set up a loop that parses messages and delivers them via
deliverInputNoCopy(length:message:isComplete:)
. To work out the message boundaries, call
parseInput(minimumIncompleteLength:maximumLength:parse:)
. Have the closure you pass to the
parse
parameter always return 0 — that is, it never consumes any bytes — but set up state that indicates whether it found a delimiter and, if so, its offset. You can then use that state outside of that call to start delivering the bytes of the message.

So, in pseudo code:

func handleInput {
    while true {
        var didFindDelimiter = false
        var countBeforeDelimiter = 0
        let parseResult = framer.parseInput(minimumIncompleteLength: 1, maximumLength: 65535) {
            … search the data for the delimiter then 
            set up `didFindDelimiter` and `countBeforeDelimiter` …
            return 0
        }
        guard
            parseResult,
            didFindDelimiter || countBeforeDelimiter != 0
        else {
            // Nothing to deliver right now.
            return 0
        }
        let didDeliver = framer.deliverInputNoCopy(length: countBeforeDelimiter, message: …, isComplete: didFindDelimiter)
        guard didDeliver else {
            return 0
        }

        if didFindDelimiter {
            … if you don’t want the delimiter to appear in the message, 
            you’ll need to call `parseInput(…)` again and discard it …
        }
    }
}

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for the insight Quinn! What is the expected behavior / best course of action if the total message size exceeds the maximumLength passed to parseInput()?


If the input coming across the wire exceeds the maximum length for parseInput(), should the cursors position in the stream persist across subsequent calls to handleInput()? For example, if maximumLength is set to 16 bytes and I have a message that's 48 bytes -- should calling deliverInputNoCopy() with a length of 48 (in the third call to handleInput() where the delimiter is encountered) deliver the 48 byte message?


Thanks!

Hi Quinn,


I am also attempting to retrieve messages that are delimited by a single character. I've taken the approach you describe above with partial success. In my case, I have a number of messages of varying length embedded in the buffer and I've struck a problem where there exists a partial message at the end of the buffer (2 bytes worth), with the remaining bytes to complete the message existing in the next TCP packet (ie the next buffer).


The following code always fails and therefore handleInput returns 0

guard parseResult, didFindDelimiter || countBeforeDelimiter != 0 
else {
     // nothing to deliver right now
     return 0
}


The calling method (below) never completes.

connection.receiveMessage(completion: { (content, context, isComplete, error) in ...}


I assume that either I have to send an incomplete message via deliverInputNoCopy(length:message:isComplete:) or somehow have handleInput process the next TCP packet.


Any help would be greatly appreciated.


Kind regards


Doug