I'm trying to write a NWProtocolFramerImplementation class that will be channeled through a Framer wrapper. There are still some parts I need figuring out.
- For
handleOutput(framer: message: messageLength: isComplete), what do the last two parameters do? DoesisCompleterefer to the end of the current conversation, or the entire connection? Why would we submit amessageLengthif the data size should already be implied withinmessage? - If I parse by line breaks, is there a way to indicate if the latest line is the last of the current conversation, either input or output?
For handleOutput(framer:message:messageLength:isComplete), what do the last two parameters do?
An app using Network framework can send a message in one go or in chunks. If the do the latter then they’ll make multiple send calls and only on the last one set the is-complete flag. The framer design reflects that:
- The system sets
messageLengthto the length of the message so far. - And
isCompleteif this is the last chunk of the message.
The best documentation for this stuff is the doc comments in the <Network/framer_options.h> header. Quoting that:
message_length— The length of the data associated with this message send. If the message is not complete, the length represents the partial message length being sent, which may be smaller than the complete message length.is_complete— A boolean indicating whether or not the message is now complete.
If I parse by line breaks, is there a way to indicate if the latest line is the last of the current conversation … ?
I don’t understand this question, but lemme set some general guidance here. If you create a line-based framer then every line is a message. Given that, there’s no higher-level ‘conversation’ concept.
Alternatively, you can build a framer that works at a higher-level. For example, IMAP server responses are made up of multiple lines, with a * prefix indicating an initial line and a status code prefix indicating the end of the response. You could build a framer that does that parsing and produces an entire response as a message. Whether that makes sense kinda depends on the specifics of the protocol.
One thing to keep in mind here is that, if you use the newer NetworkConnection API, the send and receive methods are Swift async methods, so there’s less of a need for a framer inside the connection. Rather, you can do the equivalent work outside, by writing wrappers around those methods. For example:
func processCommands(connection: NetworkConnection<TCP>) async throws {
var buffer = Data()
repeat {
let (d, _) = try await connection.receive(atMost: 1024)
buffer.append(d)
if buffer.count > 16384 {
throw POSIXError(.ENOTTY) // It’s traditional!
}
while let lfI = buffer.firstIndex(of: UInt8(ascii: "\n")) {
defer { buffer = Data(buffer.suffix(from: lfI + 1)) }
let line = buffer.prefix(upTo: lfI)
try await processCommand(line)
}
} while true
}
IMPORTANT It should be obvious that is not production quality code.
Whether this approach makes sense very much depends on whether you can rely on the new API and whether this concurrency shape makes sense for your protocol.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"