1
votes

I have a iOS client application that creates NWConnection over TCP.

connection = NWConnection(host: hostTCP, port: portTCP, using: .tcp)
        connection?.stateUpdateHandler =
        { [self] (newState) in
            print("This is stateUpdateHandler:")
            switch (newState)
            {
                case .ready:
                    print("State: Ready\n")
                    self.sendTCP(messageToTCP)
                    self.receiveTCP()
                case .setup:
                    print("State: Setup\n")
                case .cancelled:
                    print("State: Cancelled\n")
                case .preparing:
                    print("State: Preparing\n")
                default:
                    print("ERROR! State not defined\n")
            }
        }
        print("connectToTCP: Connection to server started\n")
        connection?.start(queue: .global())

And the receiveTCP() function is as follows:

func receiveTCP()
    {
        connection?.receiveMessage
        { (data, context, isComplete, error) in
            if (isComplete)
            {
                print("Receive is complete")
                if (data != nil)
                {
                    let backToString = String(decoding: data!, as: UTF8.self)
                    print("Received message: \(backToString)")
                }
                else
                {
                    print("Data == nil")
                }
            }
            else
            {
                self.receiveTCP()
            }
        }
    }

I am using a command line tool as a server application running on my macBook which listens to the incoming connection and sends/receives messages. Sending messages works fine from the iOS client application but the receive doesn't work. Also the client app works (both send and receive) fine over UDP. Is there anything wrong i am doing here?

In Apple documentation in https://developer.apple.com/documentation/network/nwconnection/3020638-receivemessage

its mentioned

Receiving messages allows you to deal with complete datagrams or application-layer messages without needing to reconstruct a stream. If you are using UDP, receiving a message will deliver a single datagram. If you request to receive a message on a protocol that is otherwise an unbounded bytestream, like TCP or TLS, note that this will not deliver any data until the stream is closed by the peer.

What does this mean "If you request to receive a message on a protocol that is otherwise an unbounded bytestream, like TCP or TLS, note that this will not deliver any data until the stream is closed by the peer."?

Does anyone know of a simple TCP example for iOS devices that uses NWFramework to send and receive string data to and from a server?

1

1 Answers

1
votes

TCP is a stream-based protocol. The connection is simply two streams of bytes - send & receive. There is no concept of a "message" inherent in TCP. Your application needs to impose some framing one the stream to extract structure from it. e.g. You could send "hello\n","World\n","test\n" as three separate calls to send but all of that data could be sent in a single packet to the destination; The TCP stack tries to optimise network utilisation and it is more efficient to send a single, larger, packet than multiple, smaller, packets.

Similarly, a large amount of data passed to send might be sent as multiple packets if the amount of data exceeds the maximum transmission unit (MTU) of the network. Data sent across the Internet can even be split into smaller packets if a link with a smaller MTU is encountered. All that TCP guarantees is that the bytes sent will be delivered in the order that they were sent. There is no guarantee how many packets will arrive.

What does this mean "If you request to receive a message on a protocol that is otherwise an unbounded bytestream, like TCP or TLS, note that this will not deliver any data until the stream is closed by the peer."?

It means that you will not receive any message until the server closes the TCP connection. If your server code does not close the connection after sending the response then receiveMessage will not complete. You can use receive(minimumIncompleteLength:maximumLength:completion:) to receive the response in "chunks". You would then need to re-assemble these and extract your message (For example, your message might be terminated by a new line \n - You would look through the data for new line characters in order to split the received data into discrete messages.

Apple provides the NWProtocolFramer protocol to let you create code to parse messages from a TCP stream. You implement code that conforms to NWProtocolFramerImplementation and which operates on the supplied NWProtocolFramer.Instance to extract messages from the stream and encode messages back into a stream of bytes.