C# TcpListener Gets Stuck When Called For 2nd Time

Viewed 36

I have this code to connect to a panel.

    private void conn_init()
    {
        bwConnector = new BackgroundWorker(); 
        //the int "anarg" does nothing other than avoid a compiler error.
        //I do "anang=0" during Form_Load and I never use it, so you can ignore it.
        bwConnector.DoWork += new DoWorkEventHandler((sender, args) => conn_function(anarg));
        if (bwConnector.IsBusy != true)
        {
            bwConnector.RunWorkerAsync();
        }
    }

And the conn_function that my backgroundworker runs is:

    void conn_function(int a_param)
    {
        try
        {
            Int32 port = 7050;
            server = new TcpListener(IPAddress.Any, port);
            server.Start(); 

            Byte[] bytes = new Byte[256];
            String data = "";
            while (true)
            {
                data = "";
                int i;
                i = 1;
                client = server.AcceptTcpClient();
                stream = client.GetStream();
                while (i != 0)
                {
                    data = "";                        
                    try
                    {                               
                        i = stream.Read(bytes, 0, bytes.Length);
                        stream.Flush();
                    }
                    catch (SocketException e)
                    {
                        richTextBox1.Invoke(new EventHandler(delegate
                        {
                            richTextBox1.Text = e.Message;
                        }));
                    }

                    data = System.Text.Encoding.UTF8.GetString(bytes, 0, i);
                    //stuff that deals with the data
                }
            }
        }
    }

I also have a timer that checks whether the connection is dead, and when it is dead, I run this code.

server.Stop();
conn_init();

My problem is, when I reset my device, my code can detect the disconnect and then it can reconnect, no issues. But when I reset it once more, I get the below error on this line:

//I get the error here and it says this:
//Unable to read data from the transport connection : An existing connection was 
//forcibly closed by the remote host
i = stream.Read(bytes, 0, bytes.Length);

I am pretty sure I am missing something very simple, yet too technical for a beginner in connection coding like me. Because it works after the first reset, it just fails after the second. I thank you in advance for any assistance you might provide.

1 Answers

You have a number of issue with your code:

  • You don't appear to be disposing the stream and client after it ends.
  • BackgroundWorker is effectively deprecated, you should use Task and await instead. Use a CancellationToken to stop the server.
  • Your server loop cannot handle multiple clients because it is not handing off the client to another thread/task.
  • You don't need to call GetStream inside the loop.
  • Do not swallow catch (SocketException e), it usually leaves the socket in an unrecoverable state. Instead, display the message and shutdown the socket.
async Task RunServerLoop(int a_param, CancellationToken token)
{
    TcpListener server = null;
    try
    {
        Int32 port = 7050;
        server = new TcpListener(IPAddress.Any, port);
        server.Start(); 

        while (!token.IsCancellationRequested)
        {
            client = await server.AcceptTcpClientAsync(token);
            Task.Run(() => HandleClient(client, token));
        }
    }
    catch (OperationCanceledException)
    { //
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    finally
    {
        if (server?.Active == true)
            server.Shutdown();
    }
}

async Task HandleClient(CancellationToken token)
{
    try
    {                               
        using client;
        using var stream = client.GetStream();
        Byte[] bytes = new Byte[256];
        int i;
        while ((i = await stream.ReadAsync(bytes, 0, bytes.Length, token)) > 0)
        {
            var data = System.Text.Encoding.UTF8.GetString(bytes, 0, i);
            //stuff that deals with the data
        }
    }
    catch (OperationCanceledException)
    { //
    }
    catch (SocketException e)
    {
        richTextBox1.Invoke(new EventHandler(delegate
        {
            richTextBox1.Text = e.Message;
        }));
    }
}

You would call it like this:

CancellationTokenSource _cancel;

private async void conn_init()
{
    _cancel = new CancellationTokenSource();
    await RunServerLoop(somevalue, _cancel.Token);
}

I note that you don't appear to be using any kind of framing mechanism. Do not assume that a single Send from one side will necessarily be received in a single Read on the other. Read may or may not fill the buffer, and may or may not contain data from different messages from the other side.

Related