#StackBounty: #c# #.net #tcplistener TcpClient exceptions when calling EndReceive and BeginReceive

Bounty: 250

I’m trying to implement wrapper class which will simply connect to TCP server and wait for data. Once data submitted from server – I will receive this data and pass it onto subscribers of my class.

All this works. Now I want to add external functionality to “reset” this class on a timer (force reconnect every so often) to keep connection alive. My idea is that Init method can be called as many times as needed to get socket reset. However, I do get various exceptions with this.

Class code:

namespace Ditat.GateControl.Service.InputListener
{
    using System;
    using System.ComponentModel;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;

    public class BaseTCPSocketListener : IInputListener
    {
        #region Events/Properties

        public event EventHandler<Exception> OnError;

        public event EventHandler<string> OnDataReceived;

        private string host;

        private int port;

        private int delayToClearBufferSeconds = 5;

        private TcpClient client;

        private readonly byte[] buffer = new byte[1024];

        /// <summary>
        /// Will accumulate data as it's received
        /// </summary>
        private string DataBuffer { get; set; }

        /// <summary>
        /// Store time of last data receipt. Need this in order to purge data after delay
        /// </summary>
        private DateTime LastDataReceivedOn { get; set; }

        #endregion

        public BaseTCPSocketListener()
        {
            // Preset all entries
            this.LastDataReceivedOn = DateTime.UtcNow;
            this.DataBuffer = string.Empty;

        }

        public void Init(string config)
        {
            // Parse info
            var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            this.host = bits[0];
            var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
            var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
            this.port = int.Parse(bits[1]);
            this.delayToClearBufferSeconds = int.Parse(bits[2]);

            // Close open client
            if (this.client?.Client != null)
            {
                this.client.Client.Disconnect(true);
                this.client = null;
            }

            // Connect to client
            this.client = new TcpClient();
            if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
                throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");

            this.EstablishReceiver();
        }

        protected void DataReceived(IAsyncResult result)
        {
            // End the data receiving that the socket has done and get the number of bytes read.
            var bytesCount = 0;
            try
            {
                bytesCount = this.client.Client.EndReceive(result);
            }
            catch (Exception ex)
            {
                this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
                this.RaiseOnErrorToClient(ex);
            }

            // No data received, establish receiver and return
            if (bytesCount == 0)
            {
                this.EstablishReceiver();
                return;
            }

            // Convert the data we have to a string.
            this.DataBuffer += Encoding.UTF8.GetString(this.buffer, 0, bytesCount);

            // Record last time data received
            this.LastDataReceivedOn = DateTime.UtcNow;
            this.RaiseOnDataReceivedToClient(this.DataBuffer);

            this.DataBuffer = string.Empty;
            this.EstablishReceiver();
        }

        private void EstablishReceiver()
        {
            try
            {
                // Set up again to get the next chunk of data.
                this.client.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, this.DataReceived, this.buffer);
            }
            catch (Exception ex)
            {
                this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
                this.RaiseOnErrorToClient(ex);
            }
        }

        private void RaiseOnErrorToClient(Exception ex)
        {
            if (this.OnError == null) return;

            foreach (Delegate d in this.OnError.GetInvocationList())
            {
                var syncer = d.Target as ISynchronizeInvoke;
                if (syncer == null)
                {
                    d.DynamicInvoke(this, ex);
                }
                else
                {
                    syncer.BeginInvoke(d, new object[] { this, ex });
                }
            }
        }

        private void RaiseOnDataReceivedToClient(string data)
        {
            if (this.OnDataReceived == null) return;

            foreach (Delegate d in this.OnDataReceived.GetInvocationList())
            {
                var syncer = d.Target as ISynchronizeInvoke;
                if (syncer == null)
                {
                    d.DynamicInvoke(this, data);
                }
                else
                {
                    syncer.BeginInvoke(d, new object[] { this, data });
                }
            }
        }
    }
}

Client code (under button click on form)

private void ListenBaseButton_Click(object sender, EventArgs e)
        {
            if (this.bsl == null)
            {
                this.bsl = new BaseTCPSocketListener();
                this.bsl.OnDataReceived += delegate (object o, string s)
                {
                    this.DataTextBox.Text += $"Base: {DateTime.Now} - {s}" + Environment.NewLine;
                };

                this.bsl.OnError += delegate (object o, Exception x)
                {
                    this.DataTextBox.Text += $"Base TCP receiver error: {DateTime.Now} - {x.Message}" + Environment.NewLine;
                };
            }

            try
            {
                this.bsl.Init("192.168.33.70|10001|10");
                this.DataTextBox.Text += "BEGIN RECEIVING BSL data --------------------------" + Environment.NewLine;
            }
            catch (Exception exception)
            {
                this.DataTextBox.Text += $"ERROR CONNECTING TO BSL ------------{exception.Message}" + Environment.NewLine;
            }
        }

Exceptions I get. First exception when button clicked 2nd time in from handler in DataReceived

The IAsyncResult object was not returned from the corresponding
asynchronous method on this class.

On following clicks I get exception from handler in EstablishReceiver

A request to send or receive data was disallowed because the socket is
not connected and (when sending on a datagram socket using a sendto
call) no address was supplied

How do I properly ensure socket closed and re-opened?


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.