Link to home
Start Free TrialLog in
Avatar of henrythacker
henrythackerFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Client / Server App

I asked a question a little while ago when needing to build a client/server app for a client. Its basically a support service whereby the user will download a client application from the website and then that will connect to a server application running on my customers local PC. I wrote a version which worked beautifully until i managed to release a version which contained a bug which i couldn't locate, and now the service is down. I want to recode the whole project, and I need to take more care this time to avoid problems. I would really like to create both sides so they are threadsafe, and a queuing system is implemented so the server operator may choose a client to speak to, while the other connected clients are in transitional state with text such as: "Please wait while you are transferred to the next available operator". I was wondering if anyone could give me some help, how to setup the sockets so they are thread safe and implement a system whereby i keep track of the clients, and can connect at will to them. I used Indy last time, but I ran into many problems because of the blocking aspect of the ports, sometimes the app would crash out, i feel non-blocking sockets if possible would allow me to handle errors more elegantly.. however, i am totally at your expertise and any similar previous experiences would be great. Code would be the best. Also, anyone know of a good autoupdate system / code.. bearing in mind i need rock solid stability. Thanks for your help.
Avatar of Tyrsis
Tyrsis

Wow, you're asking for a lot :)

I've done client/server applications for a long long time now, so I have a bit of advice for you.  First off both methods (blocking vs nonblocking) have their upsides and downsides.  It just depends on how comfortable you are with certain aspects of coding for them.  Let me first address one issue you brought up - not being able to locate a bug.  Not entirely sure the nature of the bug, but I would suggest first off getting good debugging tools.  This includes two things, first off MemProof (which is free), which is a great memory leak locator.  When you're creating a server application that runs 24/7 this is something you must watch out for.  Leaking memory can cause many headaches.  Next, Eurekalog is a great exception catching program that gives you line numbers and locations of crashes / bugs.  A great tool (and no I'm not advertising these, just suggesting them as great tools to have).  Last but not least, if you have a high capacity server that will be running for long periods of time, you will need a memory management replacement.  Delphi has an internal memory manager that is less than adaquate when it comes to applications with long running times.  It never returns allocated memory back to the system, and after awhile it can become extremely fragmented leading to the process and system to run out of memory.  

That being said, let's address sockets.  Sockets themselves are threadsafe, so there is no worry about using them in threads whatsoever.  It's the code around the sockets that can lead to problems.  This is where good debugging tools comes into play.  Creating a blocking server with threads can be a nightmare to debug if you have threads that aren't synchronized well.  If you aren't comfortable with threads, then an asynchronous (nonblocking) server is the way to go.  That being said, there is a lot more that needs to be coded, to ensure things run smoothly.  Since all sockets are in a single thread, if there are any problems in your code, the entire server can come to a halt, whereas if a thread crashes in blocking mode, you only lose that one thread (client) and not the entire server.  

I've used and seen many components for Delphi that are socket components, and in the end I just end up building from the ground up.  Most of them contain things that not only slow them down, but I wouldn't even use or think of using.  Indy is a good set of components, but has gotten a tad bloated for me.  I would take these components (Indy, ICS, even Delphi's TClientSocket code) and use these as a starting point.  Create your own socket ojects, and keep the objects simple, and just to what you need specifically.  If you don't need UDP, don't code any UDP, etc.  Test these objects thoroughly first and make sure they work well, and then build the application off of a solid foundation.  

So basically:

Blocking Sockets advantages
=======================
- Easy to implement
- Perfectly suited for threads
- Easy to debug

Blocking Sockets disadvantages
==========================
- Must be very comfortable with threads and synchronizing threads
- There is an OS limit of the amount of threads a process can create (2048 max), so if you expect to have more than that many sockets at once, then things get tricky (peered distributed servers)

Nonblocking Sockets advantages
==========================
- Can be used in a single thread, maximum thread limit is no longer a factor
- A bit easier to debug if synchronizing threads is an issue

Nonblocking Sockets disadvantages
=============================
- Harder to implement working code due to the nature of it.  You have to poll all your sockets constantly to see if data is waiting to be read, or if data is waiting to be written.  It can become a nightmare if not planned properly
- If a crash occurs in your socket code, it can take down the entire application instead of just a single client. (threaded vs nonthreaded)

Some things to think about:

- When sending and receiving data, you may want to have a way to easily pre-process and post-process data that is coming in a socket.  Why would you want to do this?  Say for example at one point you want to implement encryption or compression.  If you have it setup from the start to allow this to happen, then you will save yourself a lot of trouble.

- Cross-platform developement.  Will this ever be an issue?  If so, avoid win32 api at all costs.  The delphi TClientSocket is a good example of showing you which code can be used in linux and which code can't.  

- Internal buffering is something you should implement right inside your component, it just makes things easier.  Internal buffer being, say you have your socket component TMySocket.  And I want to send a large chunk of data that is say 500k in size.  Have something internal in your component that buffers that data, and handles it itself.  Don't force the application to worry about checking write status of sockets, sending 2k at a time etc.  Do this at the socket level and buffer it yourself.  This makes it so that an application you make that uses your socket just has to call something like TMySocket.SendData(szData);  and szData can be as large as you want, but your socket component buffers and sends that data gracefully for the application (sending back status events to tell the application how much has been sent / how much is remaining in the buffer)

Here are some links to some of the things I mentioned above:

Eurekalog - http://www.eurekalog.com
Memproof - http://www.automatedqa.com/downloads/memproof.asp

There are few different memory managers I use.  http://www.digitaltundra.com/bigbrain.php is one.  http://www.nexusdb.com has one as well.  There is also a free one that I don't have a link for right now which is called RecyclerMM.  I'm sure a google search for it will find it.  

You already have Indy which is a good blocking example of sockets.  ICS is free and located at http://www.overbyte.be/frame_index.html.  It is an Asynchronous (nonblocking) example of how to do sockets, and it includes the source.

I hope some of this hints / tips help!

Tyrsis
Avatar of henrythacker

ASKER

Woah man, that was incredible, that has answered many questions i have pondered for a long time. Thank you for that support, naturally i will reward you for such an amazingly complete response, however, i would like to just like to respond about some of the points you raised here, and perhaps that will prompt some more ideas and considerations.
Right so here we go in order!

Memory leaks i imagine were a big issue in the previous server, as I was pressurised into releasing the software quickly, i guess its the mistake of an amateur programmer to do such a thing.. although having said that the first revision worked perfectly to this end, and so it can't have been such an issue. I will certainly download the aforementioned tools and analyse the applications for any leaks.

I have come across EurekaLog too before, and am just about to go and buy it i think, it looks as if it could certainly make the whole debug process much easier, i am glad to have had this recommendation as i have considered purchasing this for ages.

Although i feel implementing a non blocking system would be easier on my part, that is not what the whole idea of programming is about, so certainly a blocking system would suit my needs better. All the advantages mentioned of making them easy to debug, not taking out the whole server app if a client crashes out, etc... makes it look exactly what i am looking for here... so i think i may consider continuing with Indy as so many people have used the Indy library for internet problems. I would not be comfortable programming a socket library with my limited knowledge of Internet programming, so I might give that a miss.. i think Indy will do fine, it is not going to need to handle thousands of requests, probably 10 at the most at one time. So any bloatedness on Indy's part will hopefully not come into the equation with such few clients.

Thank you so much for your response.. if you have any further ideas / small examples of a threaded blocking socket, although to be honest you've more than deserved your points already! All youve said about creating threads seems so perfect because the whole basis of the application is Server -> Individual Client communication, with about a maximum of 10 conversations, and I imagine threads will work well to store the conversations with each individual client. Thats another issue i had, of how to store so many conversations individually. Thanks again.
Also, sorry as a quick addition to my previous comment... i was thinking of storing the clients in a Listbox, and storing their Sockets in the Data part of TListItems. I have created a system whereby the users login using a username like in Hotmail / MSN. So is it considered good practice to do the following?

TListBox

"Connected Clients"

|-------------------|
| user@me.com  |  -> Listbox1.items[0].data = WinSocket for user's connection
|                       |
|------------------|

Thanks.
Points increased
ASKER CERTIFIED SOLUTION
Avatar of Tyrsis
Tyrsis

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thank you kindly, haven't had time to implement this model, but will attempt so now. Thank you for all your help.