Advertisement

How to recieve messages from server with TCPClient via Async

Started by November 20, 2017 02:44 AM
2 comments, last by hplus0603 7 years ago

Hi

I have set up my TcpClient to connect to my server and that works fine. But i am a bit confused how i read messages from the network stream with it continuously running via async, without ever closing the connection ?

My TcpClient Manager class has:
 


        public async Task<bool> Connect(string address, int port)
        {
            try
            {
                await _tcpClient.ConnectAsync(address, port);
                IsConnected = true;
                return true;
            }
            catch(Exception e)
            {
                Debug.Log(e);
                return false;
            }
        }
        public async Task<int> Read(byte[] readBuffer)
        {
            if (!IsConnected) return -1;

            using (var networkStream = _tcpClient.GetStream())
            {
                try
                {
                    var bytesRead = await networkStream.ReadAsync(readBuffer, 0, readBuffer.Length);
                    return bytesRead;
                }
                catch (Exception e)
                {
                    Debug.Log(e);
                    IsConnected = false;
                    return -1;
                }
            }
        }

 

So i thought to just run a co-routine and call Read constantly to get the most recent message, but that doesn't make much sense to me since a co-routine would be blocked with the await. How is this actually done? The MS Docs don't have very good Async examples with the TcpClient class so i don't know fully get how to keep calling Read correctly.

Without having looked at the docs myself, two solutions come to mind:

  1. Periodic polling (if lag in the read isn't important)
  2. Spawning a new thread (if lag is important)

Have you tried either of these? If so, what was the result?

Advertisement

To receive from a TCP stream, you need to always have a read request outstanding.

Inside the completion callback, you should put the data you received into an incoming buffer, and queue another receive request.

Note that data will come in "chunks" that may have no relation to how it was sent -- TCP is not a packet based protocol, and reading from a TCP stream is a bit like reading from a file that returns some random number of the next bytes from the file each time you read some. Thus, you need to continually look at the head of your received buffer, and see if there's a full packet in there, and if so, decode and remove that packet from the buffer.

Typically, you will prefix each (binary( packet by the length (number of bytes) in the packet, or you will terminate each (text) packet with a special character sequence, like \r\n.

The fact that you immediately call await kind-of eliminates the benefit of the asynchronous TCP, though. The whole point is that, each time through your main loop, you will "read whatever is there" and append to the end of your incoming buffer, then you will decode any whole packets found at the head of the buffer. If you instead dedicate a thread to reading, you can read in blocking fashion, and then do the same decoding, although you then have the problem of sending the ready packets across threads to be correctly processed by your game logic.

enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement