Search code examples
c#multithreadingtcpbackgroundworkerclient-side

Cancelling a background worker which is continuously reading from a network stream


I have a TCP server running which spits out messages of 2 bytes at regular intervals.

I'm trying to create a client side form which connects to the server and continuously reads from the stream until I click a disconnect button on the form.

So far the client works fine except that I cannot disconnect. I set the CancellationPending to true but it seems to reset back to false before the dowork method gets a chance to set e.Cancel.

I'm also sure there must be a more acceptable way of continuously reading the stream and writing to the form - at the moment I am calling RunWorkerAsync within the Worker Completed method to achieve the loop!

    private void Disconnect()
    {
        commsWorker1.CancelAsync();
    }

    private void ReadFromStream()
    {
        try
        {
            commsWorker1.RunWorkerAsync();
        }
        catch (Exception ex)
        {
            writeToBox("Error: " + ex.Message);
        }
    }

    //background worker dowork method
    private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
    {
        if (true == commsWorker1.CancellationPending)
        {
            e.Cancel = true;
        }
        else
        {
            Byte[] dataArray = new Byte[2];
            try
            {
                _DataStream.Read(dataArray, 0, 2);
                String reply = System.Text.Encoding.ASCII.GetString(dataArray);
                e.Result = reply;
            }
            catch (Exception ex)
            {
            }
        }
    }

    //background worker workercompleted method
    private void BackGroundDisplayMessages(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            //close connection here
        }
        else
        {
            writeToBox((String)e.Result);
            commsWorker1.RunWorkerAsync();
        }

    }
}

}


Solution

  • Can't you just loop inside the background worker method?

    private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
    {
        while(true)
        {
            Byte[] dataArray = new Byte[2];
            try
            {
                _DataStream.Read(dataArray, 0, 2);
                String reply = System.Text.Encoding.ASCII.GetString(dataArray);
                e.Result = reply;
            }
            catch (Exception ex)
            {
                return;
            }
        }
    }
    

    Then upon disconnect simply close the socket. This will cause the Exception to be thrown in the while loop and you can exit gracefully through the catch block.

    Edit: Then you can update the GUI from the loop after each message is read. Make sure the handle to the control you are updating is available (assuming it's called box):

    delegate void updateDelegate(String p);
    
    private void BackGroundGetServerData(object sender, DoWorkEventArgs e)
    {
        while(true)
        {
            Byte[] dataArray = new Byte[2];
            try
            {
                _DataStream.Read(dataArray, 0, 2);
                String reply = System.Text.Encoding.ASCII.GetString(dataArray);
                box.BeginInvoke(new updateDelegate(writeToBox), reply);
            }
            catch (Exception ex)
            {
                return;
            }
        }
    }
    

    BeginInvoke is required in this case because you are trying to update the GUI from another thread, which is not allowed. This method forwards the update to the GUI thread.