What I'm still wondering is how the client-server protocol works in this
case. Since TCP is a stream based protocol, it's necessary to have some
kind of marker for where a single message ends, either using a message
length field, using an end marker, or using the connection state (close
after each message).

Basically there is no intrinsic way to know where the peer has stopped
writing to the connection and one has to be arbitrarily chosen.
leastSize will always block until either new data has arrived, or
until the connection is closed. dataAvailableForRead may miss data if
it doesn't get written/transferred fast enough. Using waitForData with
a finite timeout is slow and may still miss if it doesn't get written
fast enough.