1

Multithread programming is a new concept for me. I’ve done a bunch of reading and even with many examples, I just can’t seem to figure it out. I'm new to C# and programming.

I have a winform project with lots of custom controls I’ve imported and will utilize many tcpclients. I’m trying to get each control to be hosted on it’s own separate thread. Right now, I’m trying to get 1 control to behave appropriately with it’s own thread.

I'll show you what I have and then follow up with some questions regarding guidance.

string asyncServerHolder; // gets the server name from a text_changed event
int asyncPortHolder; // gets the port # from a text_changed event

TcpClient wifiClient = new TcpClient();

private void btnStart_Click(object sender, EventArgs e)
{
 ... // variable initialization, etc.
 ... // XML setup, http POST setup.

 send(postString + XMLString); // Content to send.
}

private void send(string msg)
{
    AsyncCallback callBack = new AsyncCallback(ContentDownload);

    wifiClient.BeginConnect(asyncServerHolder, asyncPortHolder, callBack, wifiClient);
    wifiClient.Client.Send(System.Text.Encoding.ASCII.GetBytes(msg));
}

private void ContentDownload(IAsyncResult result)
{
    if (wifiClient.Connected)
    {
        string response4 = "Connected!!"; //debug msg
        byte[] buff = new byte[1024];
        int i = wifiClient.Client.Receive(buff);

        do
        {
            response1 = System.Text.Encoding.UTF8.GetString(buff, 0, i);
        } while (response1.Length == 0);

        response2 = response1.Substring(9, 3); // pick out status code to be displayed after

        wifiClient.Client.Dispose();
        wifiClient.Close();
    }
}

If you're knowledgeable about this, I bet you see lots of problems above. As it stands right now, I always get an exception one my first iteration of running this sequence:

"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"

Why is this? I have confirmed that my asyncServerHolder and my asyncPortHolder are correct. My second iteration of attempting allowed me to see response4 = "Connected!!" but I get a null response on response1.

Eventually I'd like to substitute in my user controls which I have in a List. I'd just like to gracefully connect, send my msg, receive my response and then allow my form to notify me from that particular control which plays host to that tcp client. My next step would be link up many controls.

Some questions: 1) Do I need more TCP clients? Should they be in a list and be the # of controls I have enabled at that time of btnStart_Click?

2) My controls are on my GUI, does that mean I need to invoke if I'm interacting with them?

3) I see many examples using static methods with this context. Why is this?

Thanks in advance. All criticism is welcome, feel free to be harsh!

Shen
  • 644
  • 5
  • 16
  • 1
    If you've got .NET 4.5 available, I'd strongly suggest using the `await` pattern instead. It's much more straightforward, and it makes handling the asynchronous I/O and the exceptions much easier. You can even easily avoid manual invokes if you make sure the asynchronous continuations end up on the GUI thread on their own. In any case, I'd like to point out a misconception you seem to have - what you're doing doesn't require multi-threading at all. And you really don't want to have more than a single GUI thread, really. Also, don't use fields to transfer data between threads, that's bad. – Luaan Jun 23 '14 at 14:23
  • @Luaan I will look in to the async/await patterns more closely and see if I can apply them here. Can multi-threading still be avoided if I have 100 controls all firing simultaneously? All they're doing is reporting status of their own individual connections (timing response, status code, etc). Thank you for your response. – Shen Jun 23 '14 at 14:27
  • 1
    `...feel free to be harsh!` - okay, thanks... **Forget** useless dinosaur archaic deprecated winforms and use proper, current, relevant technology. winforms forces you to do too much code to achieve less and you'll always be struggling with these things. See [my explanation](http://stackoverflow.com/a/21884638/643085) of how this can be achieved in a really simple reusable manner that allows you to write less code and achieve more, using relevant .Net Windows UI technology... – Federico Berasategui Jun 23 '14 at 14:51
  • Well, using more threads is rarely the best solution, especially with regards to I/O. The only valid reason to have multi-threaded GUI would be if you were saturating your CPU with work done on the GUI thread - and that would usually simply mean that you're doing something very wrong :) In fact, when you use `Invoke`, you're basically adding an item to a queue - it's a very efficient pattern to handle GUI updates. – Luaan Jun 23 '14 at 15:45
  • @HighCore Thanks. I'll have to look at this technology in the future. I'm trying to take all this one step at a time. – Shen Jun 23 '14 at 16:50

1 Answers1

0

BeginConnect returns immediately. Probably, no connection has been established yet when Send runs. Make sure that you use the connection only after having connected.

if (wifiClient.Connected) and what if !Connected? You just do nothing. That's not a valid error recovery strategy. Remove this if entirely.

In your read loop you destroy the previously read contents on each iteration. In fact, you can't split up an UTF8 encoded string at all and decode the parts separately. Read all bytes into some buffer and only when you have received everything, decode the bytes to a string.

    wifiClient.Client.Dispose();
    wifiClient.Close();

Superstitious dispose pattern. wifiClient.Dispose(); is the canonical way to release everything.

I didn't quite understand what "controls" you are talking about. A socket is not a control. UI controls are single-threaded. Only access them on the UI thread.

Do I need more TCP clients?

You need one for each connection.

Probably, you should use await for all blocking operations. There are wrapper libraries that make the socket APIs usable with await.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Thanks for the feedback. My understanding is that my send method does the connecting before sending content. I'm still unclear how to remedy this issue. It seems the general consensus is to get in the habit of using await. I appreciate it. – Shen Jun 23 '14 at 16:49
  • BeginConnect starts to connect. It does not wait for that to happen. That is the point of the "Begin" part. – usr Jun 23 '14 at 16:54