Hi. I am trying to write server side windows forms application that supports multiple clients. What I want is following:

1) I need an infinite while loop to accept new connections. In order to do that I start new thread which executes while loop. I can't execute while loop without new thread because my user interface would be blocked.

2) When new client connects to server I want to dispatch him in another thread, so every user has its own thread.

3) In that thread (one thread per user) I start client-server communication. Client first need to authorize himself with username and password. Server checks database, and if password and username are ok, they continue communication.

4) Server user interface has DataGridView that is updated with every new client that logged with correct username-password pair.

I managed to do this with BackgroundWorker, but I am not sure it is good solution. I will present my soulution, and then I will ask some questions that are bothering me.

Note: I will post just relevant part of code


First in windows forms class I define two event handlers for my background worker inherited class (so I can update form controls), and in Form_Load event I start first background worker:

private void Form1_Load(object sender, EventArgs e)
        {
//          Doorman is inherited from BackGroundWorker...I will show code later 

            Doorman doorman = new Doorman();
            doorman.ProgressChanged += OnProgresChanged;
            doorman.RunWorkerCompleted += OnWorkCompleted;
            doorman.RunWorkerAsync();
            dataGridView1.DataSource = bList;     //blist is BindingList       
            
        }

        private void OnProgresChanged(object sender, ProgressChangedEventArgs e)
        {
            List<User> list = new List<User>();
            list = e.UserState as List<User>;
            
            // Update BindingList so DataGridView shows all users
            lock(locker)
            {
                bList.Clear();
                foreach (User user in list)
                {
                    bList.Add(user);
                }
            }
        }

        private void OnWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
         //    cleanup job here
        }

Next I have two classes. Each of them inherits BackgroundWorker. First is called Doorman. Its job is to listen for connections and start new thread (through MyBackgroundWorker class that also inherits BackgroundWorker) for every connected client. Also it keeps track of all connected users.

class Doorman:BackgroundWorker
    {
        private List<User> userList;
        private int numberOfClients;
        Object locker; 

        public Doorman()
        {
            WorkerReportsProgress = true;
            WorkerSupportsCancellation = true;
            userList = new List<User>();
            numberOfClients = 0;
            locker = new object();
        }

        protected override void OnDoWork(
                                      DoWorkEventArgs e)
        {
            Socket socket = new  
                     Socket(AddressFamily.InterNetwork,
                     SocketType.Stream, 
                     ProtocolType.Tcp);

            IPEndPoint localEP = new  
                     IPEndPoint(IPAddress.Any, 50000);
            socket.Bind(localEP);
            socket.Listen(5);

            while (true)
            {
                
                //here goes code dealing with cancelation                

                Socket workerSocket = socket.Accept();
                MyBackgroundWorker bgWorker = new  
                                 MyBackgroundWorker();
                bgWorker.ProgressChanged +=  
                                    OnProgressChanged;
                bgWorker.RunWorkerCompleted += 
                                      OnWorkCompleted;
                bgWorker.RunWorkerAsync(workerSocket);
                
                
                                
            }
        }

        private void OnProgressChanged(Object sender, ProgressChangedEventArgs e)
        {
            lock (locker)
            {
               
                if (e.ProgressPercentage == 1)
                {
                    User user = e.UserState as User;
                    userList.Remove(user);
                    numberOfClients--;
                }
                else
                {
                    User user = e.UserState as User;
                    usertList.Add(user);
                    numberOfClients++;
                }
                ReportProgress(1, userList);            
            }
        }

        private void OnWorkCompleted(Object sender, RunWorkerCompletedEventArgs e)
        {
             //cleanup code
        }
    }

As you can see, I create instance of MyBackgroundWorker inside Doorman code.
MyBackgroundWorker has job to communicate with clients over tcp sockets. If client send correct username and password MyBackgroundWorker calls ReportProgress with User as second parameter and number 1 ad first parameter. I use first parameter as signaling code which is interpreted inside Doorman's OnProgressChanged handler.

Here are some parts of MyBackgroundWorker class:

class MyBackgroundWorker:BackgroundWorker
    {
           // . . .          

        protected override void OnDoWork(DoWorkEventArgs e)
        {
            socket = e.Argument as Socket;

            //Here goes communication with client code,
            // checking username and password in database, 
            // responding to client messages in a loop etc.
            // If client is logged in with correct username-password,
            // ReportProgress(1, user); is called.
            // User is litlle helper class that contains
            // username, first name, last name, and Socket of that user
        }       
  
           // . . .

I hope I extracted all relevant parts of code, so you can understand how I "designed" my solution. This is my first multithreading application and I know this is probably not good solution. Thats why I would like to hear from you how would you solve it?

I also have some questions about background worker and above code:

1) Every class that make instance of BackgroundWorker (or inherited class) needs to implement event handlers. Are those handlers always called in thread where BackgroundWorker instance is created? I guess yes, because thats probably why updating windows forms controls is working. If answer is yes, how authors of BackgroundWorker class forced those handlers to execute in specific thread without use of Invoke?

2) In my Doorman class I have private List<User> member. Just to play safe I locked part of code in OnProgressChanged handler where I update that list with new users. Did I have to do this? If that event handler is always called from same thread, I suppose there is no need for lock on private list? Also, I am courious when those handlers are executed? Is there some sort of queue and they are executed one by one as different MyBackgroundWorker instances call ReportProgress()? Because my Doorman.OnDoWork function is actually infinite loop, I dont understand when event handlers are executed?

3) Is it bad idea to use "percentage" argument from ReportProgress in OnProgressChanged handler as some sort of ID for communication between backgroundworker thread and caller? (I used it as signal that tells me if I need to add new user (passed as second argument) in list, or I have to remove it).

Thats everything for now.
Thanks in advance!

p.s. sorry for my bad English, it is not my first language :)

Recommended Answers

All 3 Replies

Hi Sarevok19,

Wow, you have set yourself quite a project for a first time with multithreading.

I think there is nothing wrong with using BackgroundWorker for what you are doing.
There are many pitfalls when using multiple threads and the BackgroundWorker protects you from some of them. Also, it comes with a nice reporting mechanism (ReportProgess), and can be asynchronously cancelled from the calling thread ().:cool:

My answers to your questions:

1) Are those handlers always called in thread where BackgroundWorker instance is created?

No. The BackgroundWorker inspects the delegates and will invoke if required; otherwise the delegate is called on its own thread.
In your case what this means is that MyBackgroundWorker will call Doorman.OnProgressChanged on the same thread as MyBackgroundWorker.OnDoWork and Doorman will invoke Form1.OnProgresChanged to the UI thread (i.e Form1's thread)

Below is an example of how this can be achieved. My OnError event invokes the delegates if needed and also protects itself from errors by wrapping each call in a try block.

public event ErrorHandler Error;

        public delegate void ErrorHandler(object sender, ErrorEventArgs e);

        protected virtual void OnError(ErrorEventArgs e)
        {
            // get local copy of event handler to ensure no changes while invoking
            ErrorHandler handler = Error;
            if (handler != null)
            {
                // translate to individual single cast delegates
                // and either invoke or call directly as required
                foreach (ErrorHandler singleCast in handler.GetInvocationList())
                {
                    ISynchronizeInvoke syncInvoke = singleCast.Target as ISynchronizeInvoke;
                    try
                    {
                        if ((syncInvoke != null) && (syncInvoke.InvokeRequired))
                            syncInvoke.Invoke(singleCast, new object[] { this, e });
                        else
                            singleCast(this, e);
                    }
                    catch
                    { }
                }
            }
        }

2) ... Just to play safe I locked part of code in OnProgressChanged handler where I update that list with new users. Did I have to do this?

No harm doing it. Unless you are doing something that could cause a Deadlock; which you don't seem to be doing. Also, Doorman.OnProgressChanged method is called on a ThreadPool thread and so could execute at any time making this a safe thing to do.

2) ... I dont understand when event handlers are executed?

OK. An event is actually a collection of delegates (pointers to functions) called a MulticastDelegate. When the event is raised, the list of delegates is called in turn, either on the current thread or invoked to the UI thread. See my OnError example above.
Generally, the .Net objects call events on the objects's own thread (the BackgroundWorker is an exception to this rule).

3) Is it bad idea to use "percentage" argument from ReportProgress in OnProgressChanged handler as some sort of ID for communication between backgroundworker thread and caller?

Go ahead, its just a number after all. ;)

One bit of advice, put try..catch blocks around sensitive bits of your code. It can help stop your threads from just dying without notice.

Good luck with your project. :)

Thank you very much for your reply. You helped me a lot to understand whats going on under the hood.
Yes, I use try/catch around sensitive bits of code. I just dont use them in background worker OnDoWork method, because I check exceptions from that method in OnWorkCompleted handler, as suggested in BackgroundWorker documentation.

I managed to finish this project last night, and everything is working very well :)

Glad i could help.
If solved then please mark solved.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.