2006. augusztus 4., péntek

Redefining TCP/IP Client...


Problem/Question/Abstract:

How do we create a TCP/IP Server/Client in Delphi?

Answer:

This article is a redefined version of my previous article "Making an application a TCP/IP Client(with sample code)". May be this is of late but hope it’s helpful. In this article, I tried to explain/discuss, in general, how to create TCP-IP clients and servers with Delphi.

What I tried to demonstrate in that sample code?

I tried to demonstrate a simple TCP-IP client application that connects to a specific port on a TCP-IP server and exchanges data. I did not say anything about the server except that my application was talking to a TCP-IP server written in Java. But all you need to talk to a server is the address/host and port/service details. And to talk to the server, you have to make sure that the server is running. That’s it. You don’t need to worry about how that server is implemented unless you want to develop both the server and client by yourself.  In this article, let me give you a brief overview of how to write a TCP-IP server in Delphi.

This is what I have done in that source code:

Established a connection to the server when the application starts.
Used a SendXml procedure to send data to the server.
Used OnClientSocketRead event to read back the data from the server.
Used OnClientSocketError event to catch the errors

You will see a Boolean flag called fWaiting in both the SendXml and OnClientSocketRead procedures. In my application, I will send some data to the server and wait for the server to respond back. I’ll essentially wait for the OnClientSocketRead event to happen hoping that the server responds ASAP. That is the reason I used the Boolean flag fWaiting.

Where to start to have an idea of what TCP-IP client and TCP-IP server?

For people who want to try, I would suggest them to have a look at the demo project called Chat.dpr in both Delphi 5 and 6.  Following are the paths where you can find the demo project:

Delphi 5:
C:\ Program Files\Borland\Delphi5\Demos\Internet\Chat\chat.dpr

Delphi 6:
C:\ Program Files\Borland\Delphi6\Demos\Internet\Chat\chat.dpr

What is basically a TCP-IP client?

It’s an application that connects to a specific port on a TCP-IP server and exchanges data either as a stream or text.

What do you need to create one with Delphi?

All you need is a TClientSocket component available on the internet palette and set the following properties:

Address: You can enter the IP address of the TCP-IP server that this client connects to.

Host: Instead of Address you can enter an alias name for the IP address here in the Host property. It’s obvious to set either the Address or Host property. But what happens if you set both? Host property takes precedence over the Address property.
Setting the Host property would be better compared to the Address property since even if you change the TCP-IP server to some other machine or change the IP address to something else, as long as you keep the alias name same, you are fine; you don’t need to worry about changing it on the TCP-IP client. (Even though it’s a slight overhead of resolving the host name to its corresponding IP address, it’s worth having it)

Port: It’s a valid integer port number where the TCP-IP server listens. (and responds.) It’s always a fixed number assigned by the server. To this port number, the client will connect to and send and receive data.

Service: As Host and Address properties are linked together in one way, Port and Service properties are also linked. In general, Service is something the TCP-IP server can provide to its clients like http, ftp. These standard services have been assigned a specific port number on the server. (e.g. http – port 80). How that works? On the server, there is a services file which maps services to their respective ports. So like that, you can have your own service description that maps to a specific port number on the server. In that case, the client can connect to the service using the Service property and interact.

ClientType: This property determines whether the interaction between the server and client occurs synchronously or asynchronously.

ctNonBlocking – The default. This indicates that the interaction between the server and client occurs asynchronously. i.e. the client can send data to the server and wait for the OnRead data to occur whenever the data is sent back from the server.
ctBlocking – This is used for interaction to occur synchronously between the server and the client.

When to make the client code thread-safe?

If more than one request is sent to the server from a client application at a time, then the client code should be thread-safe. Otherwise, the request-response may collide.


TCP-IP Server
TCP-IP Client 1
Code should be
Multiple requests at a time
Thread-safe
Code should be thread-safe
ServerType = stThreadBlocking
ClientType = ctBlocking

          
Fig. 1. A TCP-IP server receiving multiple requests from a single client at a time.

TCP-IP Server
TCP-IP Client 1
Code should be
Single request at a time
Thread-safe
Code need not be thread-safe
ServerType = stThreadBlocking
ClientType = ctNonBlocking

TCP-IP Client 2
Single request at a time
Code need not be thread-safe
ClientType = ctNonBlocking


Fig. 2. A TCP-IP Server receiving multiple requests from various clients at a time.

When to make the server code thread-safe?

If more than one request is received either from a client application or different client applications at a time, then the server code should be thread-safe. This is applicable to all TCP-IP servers written in other languages also.

Hope I’m making myself clear on the thread-safe part.

Which is the best place to put the TClientSocket component in an application?

You can put the component either in a Form or DataModule. If you just have a single form in the client application that talks to a server, then it’s okay to put the TClientSocket component in that form. But if you have many forms in the client application, then it’s better to put the component in a DataModule and use that DataModule wherever needed. I used a DataModule in my client application since I had many forms each talking to the server at a different point of time.

How do you establish a connection to the server?

Set all those properties mentioned above and then set either the Active property to true or call the Open method. Once you establish a connection, you can send the data using either the SendText or SendStream method. And you can use the OnClientSocketRead event to read the data back from the server.

What is a TCP-IP server?

It’s an application that listens at a particular port and responds to clients. It could be any standard servers like http, ftp or a custom server made for your specific application.

What do you need to create on with Delphi?

As with the client, you need a TServerSocket component with the following properties set:

Port: You can assign any valid integer value. To this port, the client can connect to and interact with the server.

Service: As I told previously, you can have a service name associated with a port number.

Whenever we say server, it should be able to serve more than one client obviously; then only it makes sense to have a server. Right? Now the next question is how these clients are talking to the server: more than one client at a time or one client at a time. It leads to the following property setting that determines whether the clients talk to the server synchronously or asynchronously.

How do we handle more than one client requests at a time?

The answer is to spawn a new thread for each client request. This can be achieved by setting the ServerType property to stThreadBlocking. Does spawning and destroying a thread for each client request an overhead? Yes. Obviously. But if our application design requires it, then there is no other way; you have to have that overhead. Can we reduce that overhead of creating and destroying threads? Yes. We can. How? Cache those threads. Right.
The ThreadCacheSize property serves that purpose. The default value is 10 but this value depends on your client application needs. You should be very careful in setting this value. If you set it to a maximum value, you will end up in memory problems. If you set it to a very low value, the client will have a wait time for each request. So you have to determine a best value based on the client statistics.

If the client requests are coming one at a time, then you can set the ServerType property to stNonBlocking.

How do we read data back from the client?

As with ClientSocket, we have OnClientRead event to read data from the client among other events.

That’s all. Hope you can write yourself both a client and server with this information.

Nincsenek megjegyzések:

Megjegyzés küldése