The example we used in the previous article is fine, but it can never be a real world application. It is not thread safe, and it does not identify all connected clients clearly enough. In this final part of the series, we will modify the application we have created in the earlier parts so that it can be used in the real world.
Delphi components by their very nature are not thread safe. It is therefore not advisable to try to access them from a thread-based application. For instance, a TMemo component cannot be referenced directly from an idHTTPServer objects' methods such as Get, Post or Command Other; if this is done an exception is generated and the program appears to hang.
We know from our previous discussions that all indy components are thread-based, since indy was designed around threads. So how do we make an application thread safe? Below is an example of code (from our client application) that is not thread safe:
We make our application thread safe by using thread safe classes such as the VCL's TThread class that passes information from a secondary thread to the main thread. The main thread in this case is your application.
Windows designates a thread every time you start an application. You can verify this by starting an application, for example notepad.exe, then opening up the task manager (Ctrl+Alt+Del). You will see notepad.exe on the list under processes.
In our client application, we have a tmemo component that we use to display quotes and dates in. This component is owned by the "main" thread, therefore it has direct access to this component. So there is no question of it not being thread safe.
The moment a secondary thread is introduced into this equation, and that secondary thread wants to access components owned by the main thread, you have to find a way to enable that access. Before we move on to discuss how to enable that communication between main and secondary threads, let's define what a secondary thread is. A secondary thread is any thread that runs within the main thread. So in our case, indy components, such as the idTCPServer component, will start secondary threads within our application.
So any thread that is not the main thread is a secondary thread. Now that we got that out of the way, let's get back to what methods are used to enable the passing of information between the main and secondary threads. The VCL's TThread class contains a method called Synchronize that enables the passing of information between the main and secondary threads. We will use this method when we rewrite our previous example in a moment.
Identifying Connected Clients
Identifying connected clients might not be a big deal in most implementations of a client server application, because each client that connects to a server will have an IP address and that is all that is needed to respond to a client's request. But in other situations you will need an IP address and something else to differentiate between clients. For example, in a chat application scenario you would want to identify clients by their nicknames and not by their IP addresses, because two (or more) clients can connect from the same computer and if you do not have anything else to differentiate between them, the server application is going to send the message to both of them, instead of to just one of them.
So what can we do to remedy this situation? Well, for starters, we know that when a client connects to an indy server, the server spawns a new thread for it, and then services it within its own context. Indy has a class called "Context" that enables us to collect information about a client. What we will do then is derive a new class from the Context class that will hold information about the client for us:
This way whenever a client connects, we will collect this information and use it to identify the client. So let's rewrite our previous example and make it a robust client server application.