Using NWBrowser and NWListener I'm trying to send a small package of data from the listener/server to the device.
However the device never receives the actual bytes. It either:
- gets stuck the
preparing
state - the connection gets reset
- the data is null and is marked as
isComplete
=true
The only way I can get the device to receive the data is by calling cancel
on the NWConnection on the server/NWListener end.
Here is some sample code I am working with: https://github.com/leogdion/JustBonjour/tree/nwlistener
Is this expected behavior that cancel is required?
- gets stuck the preparing state
- the connection gets reset
That seems a little bit odd, usually these first two things will happen if the server that you're connecting to isn't reachable, isn't listening on the port that you're trying to connect to (could the process have crashed?), or just doesn't accept the incoming connection.
If that happens, your calls to receiveMessage
will likely deliver nil
data that is marked as complete, along with an error indicating that the connection failed and that's why there's no data (partial or otherwise, it'll try to deliver anything it has at that point).
Is this expected behavior that cancel is required?
In this case, yes!
It helps to think about the concept of a "message" when working with different protocols.
Since you're using .tcp
as your parameters, you have a protocol stack using just TCP over IP (with no security). Because TCP is a stream based protocol, there's nothing to tell the other side that you're "done" sending unless you either (a) add a protocol on top or (b) close the connection.
HTTP/1.0, for example, does the latter and sends EOF (a FIN bit on the wire) to let the other side know that the message is complete and that it's okay to handle the data.
In this case, you're telling the connection to accumulate all of that data for you and deliver it all at once when a FIN comes in. In your code, when you cancel the connection on the server (the sender of the data), that causes a FIN to be sent, and you see receiveMessage
tell you "all done, here's the whole thing", delivering you the data.
It sounds like that isn't what you're looking for, however, so you need to find some way to delineate those messages.
That could be something like adding a TLV framer (see this sample code for an example) to provide message boundaries on the wire, or it could be something as simple as having a fixed size of this message and always reading exactly that many bytes out of the TCP stream. The framer will be the most future-proof, most likely.
If you wanted to confirm that's what's happening, you could try calling receive
(docs) instead of receiveMessage
with a minimum length of something smaller than the message you're sending. If this is what's happening, you should see data being delivered without having to cancel the connection.
Note, however, that you also might get partial data delivered and need to accumulate it until isComplete
is true; that's why you'd want to add a TLV framer and use receiveMessage
so that the accumulation can be done inside the connection and you just need to handle logically "complete" messages in your business logic when you read from the connection.
This is the preferred approach and works unless you need to handle messages that are too large to hold in memory all at once. If that's the case, use receive
to read out partial bits of each message and save them to disk / otherwise consume them to make room for the rest of the message. It sounds like, for your app, that's probably not necessary, though.