locked
How do you keep your application from freezing while it is trying to connect to a remote server? RRS feed

  • Question

  • Hello, any help here would be much appreciated.

    In my program the client application connects to the server application over the internet. This works fine when I have the server application running. But if I turn the server application off (to simulate the server being down) the client application  freezes while it waits for the server to time out. I am talking freezing. In windows Vista I get the (Not Responding) message in the title bar. But then once the server times out, my client application carries on just as I have programmed it to. 

    Also, I want to display a progress bar (in marquee style) while the client is waiting on the server. But for some reason, even though I call the code to run the  progress bar before I call the code to connect to the server, the progress bar actually disappears from my form! However, it does return again once the server has timed out and control is returned to my program.

    Here are the snippets of code involved in making this happen:

    First the button event handler is called:
            private void btnLogin_Click(object sender, EventArgs e)
            {
                // Make sure username and password fields are not blank
                if (TestForEmptyLoginBoxes() == true) 
                    return;
    
                // If existing user...
                if (cbNewUser.Checked == false)
                    Login();
                else // If new user...
                    CreateUser();
            }
    Then that calls the Login() method. This method is longer but I am only showing the code relevant to my question:
    private void Login()
    {
                string username = txtUserName.Text;
                string password = txtPassword.Text;
                bool connected = false;
    
                // Connect
                try
                {
                    StartProgressBar("Connecting");
                    connected = Connect(); 
                }
                catch (Exception e)
                {
                    SetDisplay_lblError(e.Message);
                    StopProgressBar();
                }
    }
    First Login() starts the progress bar(or is supposed to start the progres bar) by calling StartProgressBar():
            private void StartProgressBar(string progressLabel)
            {   
                progressBar.Style = ProgressBarStyle.Marquee;
                progressBar.MarqueeAnimationSpeed = 40;
                UpdateProgressBar(progressLabel); 
            }
    StartProgressBar() then calls UpdateProgressBar which just changes the size of the progress bar and its corrosponding descriptive label. I know doing it that way is a little unusual but that's the way I chose to do it. Anyway, this is UpdateProgressBar():
     private void UpdateProgressBar(string progressLabel)
            {
                // Setup varibales
                Graphics g = this.CreateGraphics();
                Point pBarLoc = progressBar.Location;
                string newProgressString = progressLabel;
                SizeF oldLabel = g.MeasureString(lblProgress.Text, lblProgress.Font);
                SizeF newLabel = g.MeasureString(newProgressString, lblProgress.Font);
                int lblDif = (int)(newLabel.Width - oldLabel.Width);
                Rectangle changeRect = new Rectangle();
    
                // Chane the text box and progres bar
                lblProgress.Text = newProgressString;
                progressBar.Location = new Point(pBarLoc.X + lblDif, pBarLoc.Y);
                progressBar.Width -= lblDif;
    
                // Invalidate the controls so they are redrawn
                changeRect.Location = lblProgress.Location;
                changeRect.Height = progressBar.Height;
                changeRect.Width = (progressBar.Location.X + progressBar.Width) - lblProgress.Location.X;
                Invalidate(changeRect);
            }
    After UpdateProgressBar() completes control is returned to StartProgressBar() which then completes and controls is returned to Login(). Login then calls Connect(). It should be noted that if I comment out the call to Connect(), everything works perfectly.  But when comment runs the client application freezes for a while (which causes Vista to think my program has frozen) and then unfreezes once it's attempt to connect to the server times out. Here is the Connect() method:
            private bool Connect()
            {
                if (ChannelServices.GetChannel("chatAppChannel") == null)
                    network = new Network();
    
                network.Connect();
                return true;
            }
    Then Connnect() method creates a Network object. (I wrote the Network class myself, it's not from Microsoft). The Network constructor just registers the channel that will be used for remoting:
            public Network()
            {
                try
                {
                    chatAppChannel = new TcpChannel();
                    ChannelServices.RegisterChannel(chatAppChannel, false);
                }
                catch (Exception e)
                { throw new Exception("Remoting Error: unable to instantiate or register channel " + e.Message); }
            }
    After the Network object has been created, network.Connect() is called:
    public void Connect()
            {
                try
                {
                    string serverAddress = Dns.GetHostEntry("www.myAddress.info").AddressList.ElementAt(0).ToString();
     
                    serverObject = (ChatAppInterface.clientServerInterface)Activator.GetObject(typeof(ChatAppInterface.clientServerInterface), "tcp://" + serverAddress + ":1212/ServerObject");
                  
                    serverObject.ToString(); // test to see if the remote object is working
                }
                catch (Exception e)
                {
                    string detail = e.Message;
                    throw (new Exception("Error creating remote object. Details: " + detail));
                }
            }

    As you can see, the way I am having my client find the server across the internet is to resolve a domain name into an ip address. That is not my actual domain name. It has been changed to www.myAddress.info for anonymity. Anyway this method does work when I have the server running and I am able to create and use the remot object. I believe it is here where the program ends up freezing while it tries creating the remote object on the server. Again, the reason it is unable to create the remote object is because I am not running the server application (for testing purposes). So it times out because of that but I imagine the client application would also freeze if the user had a very slow internet connection.

    Anyway, after this method throws it's exception, control is returned to Login() which displays an error message on the form. Then you can try to log in again and go through the same thing all over again. The program doesn't crash, it just freezes and for some reason turns the progress bar inivisible even though I don't specify anywhere for the progress bar to turn invisible.

    So to sum up my question. How do I stop my appliction from freezing while it is trying to connect to a server on the internet? Also, why is my progress bar dissapearing??? If I use a thread somewhere will that help me?

    Thanks for your help
    Saturday, September 26, 2009 5:16 PM

Answers

  • You are experiencing these issues because your running all that from 1 thread.  That means when you try to connect to the server, everything stops until it times out.  At the very least use a thread to connect, but I recommend also using a thread for the progress bar too and any other long running operations.  This will keep your UI from appearing to freeze.
    おろ?
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 5:21 PM
  • Implement a BackgroundWorker . It's even easier through the designer (under Toolbox->Components). It will keep the UI thread responsive and it also has progress reporting.
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 5:25 PM
  • Do it on a separate thread like others have suggested. 

    You can create a BackgroundWorker component like this:

    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork); //Use Tab to autogenrate methods

    The moment you issue a call

    bw.RunWorkerAsync();

    the new thread will be started and your code will be executed


    One thing to take care of is when you try to update any UI (such as your prog bar) inside the thread you started, you might get a cross thread exception because the UI control was made on the main thread. In such a case you will need to delegate control to update the UI to the main thread using BeginInvoke.

    Welcome to multithreading!

    GS
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 6:26 PM

All replies

  • You are experiencing these issues because your running all that from 1 thread.  That means when you try to connect to the server, everything stops until it times out.  At the very least use a thread to connect, but I recommend also using a thread for the progress bar too and any other long running operations.  This will keep your UI from appearing to freeze.
    おろ?
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 5:21 PM
  • Implement a BackgroundWorker . It's even easier through the designer (under Toolbox->Components). It will keep the UI thread responsive and it also has progress reporting.
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 5:25 PM
  • Do it on a separate thread like others have suggested. 

    You can create a BackgroundWorker component like this:

    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork); //Use Tab to autogenrate methods

    The moment you issue a call

    bw.RunWorkerAsync();

    the new thread will be started and your code will be executed


    One thing to take care of is when you try to update any UI (such as your prog bar) inside the thread you started, you might get a cross thread exception because the UI control was made on the main thread. In such a case you will need to delegate control to update the UI to the main thread using BeginInvoke.

    Welcome to multithreading!

    GS
    • Marked as answer by WreckingBall2 Saturday, September 26, 2009 6:41 PM
    Saturday, September 26, 2009 6:26 PM