Search code examples
c#socketsasyncsocketnetworkstream

Unable to read data correctly from .Net socket in C#


I have a client and server class in C# that uses socket communication. The Server looks like this:

    public class AsyncTcpServer
    {
        private Socket _server_socket;
        private Socket _client_socket;
        private byte[] _receive_buffer;
        private byte[] _send_buffer;
        private NetworkStream _ns;


        public void Start()
        {
            try
            {
                _server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _server_socket.Bind(new IPEndPoint(IPAddress.Any, 17999));
                _server_socket.Listen(0);
                _server_socket.BeginAccept(new AsyncCallback(BeginAccept), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void BeginAccept(IAsyncResult ar)
        {
            try
            {         
                _client_socket = _server_socket.EndAccept(ar);

                _receive_buffer = new byte[_client_socket.ReceiveBufferSize];
                _send_buffer = new byte[_client_socket.ReceiveBufferSize]; 
                _ns = new NetworkStream(_client_socket);

                _client_socket.BeginReceive(_receive_buffer, 0, _receive_buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try 
            {
                string text = Encoding.ASCII.GetString(_receive_buffer);
                Debug.Print("Server Received: " + text);                
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }
        }

        public void Send(byte [] bytes)
        {
            try
            {
                _ns.Write(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }            
        }
    }

And the client looks like this:

    public class AsyncTcpClient
    {
        private Socket _client_socket;
        private byte[] _buffer;
        private const int HEADER_SIZE = sizeof(int);

        public void Start()
        {
            _client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);         
            _client_socket.BeginConnect(new IPEndPoint(IPAddress.Loopback, 17999), new AsyncCallback(ConnectCallback), null);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                _client_socket.EndConnect(ar);                
                _buffer = new byte[_client_socket.ReceiveBufferSize];
                StartReceive();
                byte[] buffer = Encoding.ASCII.GetBytes("Connected!");
                _client_socket.Send(buffer);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void StartReceive(int offset = 0)
        {
            try
            {
                _client_socket.BeginReceive(_buffer, offset, _buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try
            {
                int bytes_processed = 0;
                int bytes_read = _client_socket.EndReceive(ar);

                if (bytes_read > 0)
                {
                    NetworkStream ns = new NetworkStream(_client_socket);

                    while (ns.DataAvailable && (bytes_processed < bytes_read))
                    {
                        byte[] len_bytes = new byte[HEADER_SIZE];
                        ns.Read(len_bytes, 0, HEADER_SIZE);
                        int current_chunk_size = BitConverter.ToInt32(len_bytes, 0);

                        if (current_chunk_size > 0)
                        {
                            byte[] data_buff = new byte[current_chunk_size];
                            ns.Read(data_buff, 0, current_chunk_size);
                            string s = Encoding.ASCII.GetString(data_buff);
                            bytes_processed += (HEADER_SIZE + current_chunk_size);
                            Debug.WriteLine(s);
                        }
                    }                                        
                }
                StartReceive();         
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }

            StartReceive();
        }
    }

They work together as follows:

  • Server starts
  • Client connects
  • Server sends custom packets to the client for its comsumption

I use the following 'data structure' to package my transmission data on the server side to send to the client:

{[DATA_LENGTH_IN_BYTES][PAYLOAD_BYTES]}

On the client side, I parse the first 4 bytes (sizeof(int)) to determine the payload length and then parse the payload itself. This works the first time I do it but after that the DataAvailable member of the NetworkStream is false and I can't parse the rest of the payload.

Why is DataAvailable false? I'm pretty new to doing this stuff in C# - am I approaching it the wrong way entirely?

Thanks in Advance!


Solution

  • Here is the solution I settled on:

    Server:

    public class Listener
        {
            private TcpListener _listener;
            private TcpClient _client;
    
            public void Start()
            {
                _listener = new TcpListener(IPAddress.Loopback, 17999);
                _listener.Start();
                _listener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), _listener);
            }
    
            private void AcceptTcpClientCallback(IAsyncResult ar)
            {
                try
                {
                    Debug.WriteLine("Accepted tcp client callback");
                    _client = _listener.EndAcceptTcpClient(ar);               
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }            
            }
    
            public void Write(string data)
            {
                try
                {
                    NetworkStream ns = _client.GetStream();
                    byte[] buffer = Encoding.ASCII.GetBytes(data);
                    ns.Write(buffer, 0, buffer.Length);
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }
            }
    
        }
    

    And the client:

    public class Client
        {
            private TcpClient _client;
            private byte[] _buffer;
    
            public void Start()
            {
                try
                {
                    _client = new TcpClient();
                    _client.BeginConnect(IPAddress.Loopback, 17999, new AsyncCallback(ConnectCallback), _client);
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }
            }
    
            private void ConnectCallback(IAsyncResult ar)
            {
                try
                {
                    NetworkStream ns = _client.GetStream();
                    _buffer = new byte[_client.ReceiveBufferSize];
                    ns.BeginRead(_buffer, 0, _buffer.Length, new AsyncCallback(ReadCallback), null);
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }
            }
    
            private void ReadCallback(IAsyncResult ar)
            {
                try
                {
                    NetworkStream ns = _client.GetStream();
                    int read = ns.EndRead(ar);
                    string data = Encoding.ASCII.GetString(_buffer, 0, read);
    
                    var res = data.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
    
                    foreach (var r in res)
                    {
                        Debug.WriteLine(r); // process messages
                    }
                    ns.BeginRead(_buffer, 0, _buffer.Length, ReadCallback, _client);
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                }            
            }
        }
    

    Where messages from the server to the client are formed as follows:

    string message = "This is a message" + "\r\n";
    _listener.Send(message);
    

    I like this alternative for its simplicity. Its a lot shorter and (to me at least) much easier to manage.

    HTH