Constructing the Interface for an Internet Access Control Application
In this third part to a six-part article that shows how to build an application for Internet access control, such as you might find in an Internet cafe, we will mainly be dealing with the functionality of the main server application. This is the application that will provide us with both the interface and capability to manage client sessions.
Constructing the Interface for an Internet Access Control Application (Page 1 of 5 )
The requirements are as follows:
The main purpose of this interface is to visually maintain communication between the server and the client applications. So for any action taken by the client application, we will get a visual confirmation.
The server application must be able to process all incoming requests from the client applications.
It must provide the staff member with the capability to allocate Internet sessions. Again, the user interface must be easy to use and guide the staff member.
Staff members, depending on their access level status, must have access to other parts of the server application.
Depending on how many client applications you communicate with at a time, it is inevitable that the server application will end up in a "bottleneck" situation, where communication between the server and, say, workstation B is slowed down or worse, ignored because the server is too busy trying to talk to workstation D. You will not get this problem if you only have a few workstations talking to the server at a time, but as a developer you should always prepare your code in such a manner that it caters to many client communications simultaneously; in other words, you should always code in such a manner that your applications can cope with future requirements and changes.
This is where threading comes in. It's also the main reason why I choose the Indy components that are available since Delphi 7. These components come with two very useful TCP components called IdCmdTCPServer and IdCmdTCPClient, both of which we will be using to power our application. Both of these components (as indeed all of Indy's components) are what we call "threadsafe."
When you normally run a program, it has only one thread, which we call the main thread, or in Delphi's case the main VCL thread. This means that all the code is executed through this one thread. Imagine we have fifty workstations all of them are sending requests to the server application simultaneously. If the server application has only the main thread, your application will almost certainly crash, because it cannot prioritize and send responses to each request that has been made.
In other words, our hypothetical server application is not thread safe, but if the server application opens up a new thread for each request, then it will be able to handle many requests simultaneously. It is for this reason that I use the Indy components. By default, Indy creates separate threads for every client connection that the server receives. And each thread has a context associated with it in the form of TidContext. By default, Context collects information about the connecting client, such as the IP address from which the client connects or the name of the local computer from which the client is connecting. All of this information is stored in the "data" property of the context class.
What we will be doing in this application is creating a custom descendant of the TidContext class and then making the server use our version. This is because we require particular types of information about the clients that connect to our server, which has to do with the overall functional requirements of the server. We will come back to the discussion about context when we look through the code that makes up the server.
In terms of the overall communication FROM the server to the client, there is actually very little of it. This is mainly because the server is basically designed to inform staff members about the status of the workstation, which it does through updating the user interface in real time. There are only two commands that the server application sends to the client.
These commands are shutdown and activate. The activate command is sent with two other pieces of information, and is the command used to remove the screen block from the workstation when a session is allocated for a user. The shutdown command shuts down all of the workstations.
Then from the client comes two communications. One is timesup. This communication is sent by a client when the user session has ended. Upon receiving this communication, the server updates the various controls on the user interface. The other piece of communication that comes from the client is when the client connects to the server at startup; the server checks to see if the client sent a name in that communication and then either registers it or disconnects a client with an error message.
Let's look at how all of the above requirements and concepts are implemented, code wise. In this next section I've listed the entire code that makes up the main part of the server application.