1

thanks to this answer I was able to determine if Server is listening on a given port or not:

How to configure socket connect timeout

now I'm trying to create an endless loop, which will be loaded on form_load event and will be constantly checking if server is listening.

here is my code:

            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IAsyncResult result = socket.BeginConnect("192.168.0.131", 1095, null, null);

            bool success = result.AsyncWaitHandle.WaitOne(500, true);

            if (!socket.Connected)
            {label3.Text = "can't use"; socket.Close();}
            else
            {label3.Text = "start action";}

If I put following code into "on_button_click" event - everything works fine (except for - I have to click the button every single time I want to refresh status)

and when I create endless loop - I'm not getting any results at all:

while (true)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IAsyncResult result = socket.BeginConnect("192.168.0.131", 1095, null, null);

            bool success = result.AsyncWaitHandle.WaitOne(500, true);

            if (!socket.Connected)
            {
                label3.Text = "can't use";
                socket.Close();
            }
            else
            {
                //success = true;
                label3.Text = "start action";
                socket.Close();
            }
        }

I guess it has something to do with threading but I just can't figure it out. What might be the problem?

Edit:

timer tick solution:

private void Form1_Load_1(object sender, EventArgs e)
        {
            System.Windows.Forms.Timer MyTimer = new System.Windows.Forms.Timer();
            MyTimer.Interval = (200);
            MyTimer.Tick += new EventHandler(MyTimer_Tick);
            MyTimer.Start();
        }

        public void MyTimer_Tick(object sender, EventArgs e)
        {

                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IAsyncResult result = socket.BeginConnect("192.168.0.131", 1095, null, null);

                bool success = result.AsyncWaitHandle.WaitOne(500, true);

                if (!socket.Connected)
                {

                    label3.Text = "can't use";
                    socket.Close();
                    //throw new ApplicationException();
                }
                else
                {
                    //success = true;
                    label3.Text = "start action";
                    socket.Close();
                }   

        }
Community
  • 1
  • 1
Alex
  • 4,607
  • 9
  • 61
  • 99

5 Answers5

2

You can add a Timer object to your form. Start the timer on the form load, and in the timer Tick event, run your code that you have in the infinite loop.

Note: DO NOT include the while(true) loop, just the code inside the loop. You really don't want an infinite loop in your GUI thread :)

EDIT
I still think you may need to consider a redesign of the app behavior, but this should be better than the Timer I suggested earlier

A somewhat better approach to the Timer (which should take care of the UI lag issue) would be for you to add a BackgroundWorker to your UI. Add a DoWork method similar to this:

void DoWork(object sender, DoWorkEventArgs e)
{
    while(!_bw.CancellationPending)
    {
        // Do your socket connection stuff

        // you can either update some member variables and call
        // the progresschanged method or you can use a BeginIvoke call
        // to update the labels, you CANNOT update the labels in this method
        if (!_bw.CancellationPending)
        {
            // Checking the cancel pending before sleeping so that we don't sleep
            // while a cancel is pending. There are better ways to do this with
            // event handles, but this should get you off and running.
            Thread.Sleep(1000);
        }
    }
    e.Cancel = true;
}

Note: If you were using .Net 4.0, you may want to consider using the task factory and a cancellation token rather than the BackgroundWorker, but either one should get you up and running.

pstrjds
  • 16,840
  • 6
  • 52
  • 61
  • +1. The timer will solve some threading headaches automatically. Nice idea! – David Mar 22 '12 at 16:05
  • first of all - thank you! I've managed to get "status update" working using Timer Ticks (see edit). However when I use this approach - my program literary becomes unusable (i.e. frozen, but working) – Alex Mar 22 '12 at 16:41
  • It's probably because of wait for the socket result to come back. Can you adjust your tick interval up. Do you really need to hammer the socket that much. What is the purpose of the check. Would it be better to just check the socket at the time of your operation that needs it? – pstrjds Mar 22 '12 at 16:54
  • Though I think that continually launching async connect requests from a timer is not the best solution, I have to +1 for 'just check the socket at the time of your operation that needs it'. – Martin James Mar 22 '12 at 17:11
  • @Martin James I agree totally. I was thinking about the locked UI issue, more than what was actually being done. That is why I added that additional comment after Alex's followup. I would not normally flood a server with connect/disconnect requests just to check if it was alive. Good grounds for admin disallowing connections from your host. – pstrjds Mar 22 '12 at 17:17
  • right, with 1000ms tick time - I can freely move form when the status is "start action", but when I turn off Server's listening - status goes back to "can't use" and form-movement becomes laggy. Now, regarding " check the socket at the time of your operation that needs it", you see, in my case **Server decides on the time, when Client should make some action**. When I send out data from the Client - Server stops the listener and analyzes data. When analyzing is finished - listener is turned back on, Client notices it, **generates** new data and sends it to Server. – Alex Mar 22 '12 at 17:18
  • 1
    @Alex The lagginess is caused by the fact that when the socket is not available you have to wait the full 500 ms timeout for the response. You really shouldn't block in the Tick handler, it blocks the UI. For something like this you really need to do the operation in a worker thread. My initial idea is wrong. It also sounds like you should consider a redesign. Shutting off the listener and then waiting for it to come back up so you can send more data seems like a bad pattern. What happens if you send data, the server receives it, but before it shuts the listener off your app sends more data? – pstrjds Mar 22 '12 at 17:24
  • then I'll just use Thread.Sleep(800); to prevent this ;) Server will stop the listener during this time, so the Client won't make "false" attempt. – Alex Mar 22 '12 at 17:47
  • @Alex There is still a race condition where the server gets the data, then shuts off the listener, but in the mean time the app has pinged the server and found the connection open and so sends more data, but now the shutdown has occurred and so there is no listener when the data gets there. – pstrjds Mar 22 '12 at 19:45
1

If that loop is executed on the GUI thread it will block the GUI because it becomes unable to receive user input.

You have to execute the code on a different thread and then update the UI using Invoke or BeginInvoke. Something like:

Thread t = new Thread(
   o => 
   {
    while (true)
    {
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IAsyncResult result = socket.BeginConnect("192.168.0.131", 1095, null, null);

        bool success = result.AsyncWaitHandle.WaitOne(500, true);

        if (!socket.Connected)
        {

            label3.BeginInvoke((Action)(() => { label3.Text = "can't use"; }));
            socket.Close();
        }
        else
        {
            //success = true;
            label3.BeginInvoke((Action)(() => { label3.Text = "start action"; }));
            socket.Close();
        }
    }
  });
t.Start();
Tudor
  • 61,523
  • 12
  • 102
  • 142
  • Tudor, thank you for your solution! It partly solved my problem. When I **turn on** server listener - status changes to "start action" for **half a second**, and for the rest of the time I see "can't use" label.. well, at least form is not frozen any more. – Alex Mar 22 '12 at 16:50
  • +1 for not suggesting timers/DoEvents and for getting all that connect-latency and WaitOne() stuff off the main thread. You may need a sleep() as well to prevent all the available sockets getting stuck in TIME_WAIT. TBH, this requirement is not good anyway - the server admin would surely not like the idea of continual connect/disconnect! – Martin James Mar 22 '12 at 17:04
  • @MartinJames my home LAN doesn't have any admins.. except for me. This app is for personal use only. Anyway, thanks for the comment.. a lot of info to think about.. – Alex Mar 22 '12 at 17:52
0

There's not enough code to tell everything we need to know, but here's a start.

Is this loop running on a different thread, or is it just there in the button_click event?

  • If it's not running on a different thread, likely you'll click the button and the app will appear to freeze, and the UI will never get updated... You need to set this up to run on a different thread.

    • You can throw an Application.DoEvents() command immediately after updating the label to unfreeze the UI and update the label, but this isn't something to do in live code. You'll want to create a new thread for your check in a production app. Application.DoEvents in this scenario would cause you problems.
  • If it IS running on a different thread, you probably need to use InvokeRequired to update the labels on the form.

This article shows code for running code on a different thread and using InvokeRequired properly.

David
  • 72,686
  • 18
  • 132
  • 173
0

I don't think you can instantiate an object more than once. You may need to put

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

before the while loop

To better diagnose your problem you could also use

Console.WriteLine("Can't use");
Console.WriteLine("Start action");

to see if there is a threading issue or not (with the gui not updating)

Lectrode
  • 412
  • 1
  • 3
  • 13
0

It takes time for a TCP connection to be closed. Each time you connect to the remote server and call close on the socket a TCP handshaking takes place.

See this guide http://www.tcpipguide.com/free/t_TCPConnectionTermination-2.htm

Avoid, opening/closing sockets at such a high rate (half a second). You could build a long list of TCP connections on the remote server that are in a close wait condition, and if this isn't your server then it could be thought of as an attack.

For failed connections, this isn't really a problem.

Reactgular
  • 52,335
  • 19
  • 158
  • 208