🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.internet.socket
20 messages
20 total messages Started by Vladimir Stefanovic Mon, 26 Sep 2005 11:47
Simple e-mail server
#3383
Author: Vladimir Stefanovic
Date: Mon, 26 Sep 2005 11:47
133 lines
4308 bytes
Hi,

I would like to make a *simple* e-mail server, and I have
a couple of dilemmas which are mainly related to stable
functioning of the whole system.

By that I mean to be sure that my threads, critical sections
and lists are coordinating properly, without collisions and
bottle necks.

My imaginary e-mail server (Service Application + TServerSocket)
should:

* receive requests from clients by ServerSocketGetThread(),
ie, the threads should just make Add()s to the TStringList


* process the TStringList (send emails) and Delete()
processed Items


QUESTION 1:
Is try/catch needed at all in case of spawning the thread in
a service (to prevent the service from shuting down because of
unhandled exception)?

void __fastcall TEmailService::ServerSocketGetThread(TObject *Sender,
      TServerClientWinSocket *ClientSocket,
      TServerClientThread *&SocketThread)
{
   try
   {
      SocketThread = new TEmailServerThread( false, ClientSocket );
   }
   catch ( const Exception &e )
   {
      // ... just swallow not to stop the service ...
   }
}


QUESTION 2:
Should I place Enter/Leave/CriticalSection here or in the
body of AddRequestToSharedStringList(), like I already did,
and is there any difference?

void __fastcall TEmailServerThread::ClientExecute()
{
   // ...
   AddRequestToSharedStringList( /* ARGS */ );
   // ...
}

QUESTION 3:
Can I trust to '__finally' or it's better to use RAII?

void __fastcall TEmailServerThread::AddRequestToSharedStringList( /* ARGS */ )
{
   try
   {
      EnterCriticalSection( &EmlSrvCriticalSection );
      try
      {
         MyStringList->Add ( /* ARGS */ );
      }
      catch ( const Exception &e )
      {
         // ...
      }
   }
   __finally
   {
      LeaveCriticalSection( &EmlSrvCriticalSection );
   }
}


QUESTION 4:
Where should I place the sending code? Is the ServiceExecute()
appropriate because if bottle neck?

void __fastcall TEmailService::ServiceExecute(TService *Sender)
{
   while ( !Terminated )
   {
      if ( !SendingInProgress )
      {
         try
         {
            EnterCriticalSection( &EmlSrvCriticalSection );
            SendingInProgress = true;

            SendEmailsFromList(); // send the *whole* list

         }
         __finally
         {
            SendingInProgress = false;
            LeaveCriticalSection( &EmlSrvCriticalSection );
         }
      }
      // This prevents Windows Task Manager to report 100% CPU usage
      Sleep ( 500 );

      ServiceThread->ProcessRequests( false );
   }
}

QUESTION 5:
In the code above, I send all the list within a CriticalSection.
Is it smarter to spawn the CriticalSection in the body of
SendEmailsFromList() and spaw it for *every* single item to be sent,
and so leave ServerSocketGetThread() to 'breathe' ?


The reason why I wanted first to store the requests in the list and
not to send them immedatelly is because I wanted:

* to keep the threads as simple as they can be
* that one request (protocol) can hold more than one e-mail request
to be sent, like |email1|email2|email3|...
* the system to be flexible if I decide to make SMS server, which will
not be as fast as sending emails.

QUESTION 6:
If all this is a bad idea can you please suggest me something better
to start with?



Best regards,
Vladimir Stefanovic
Re: Simple e-mail server
#3384
Author: Remy Lebeau (TeamB)
Date: Mon, 26 Sep 2005 17:03
105 lines
5040 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:4337dfe1$1@newsgroups.borland.com...

> Is try/catch needed at all in case of spawning the thread in a service
> (to prevent the service from shuting down because of unhandled exception)?

The service will not be shut down.  However, the TServerSocket will not
function properly anymore.  On order to use the OnGetThread event in the
first place, the TServerSocket has to be set to ServerType=stThreadBlocking.
As such, the TServerSocket uses a worker thread to listen for and accept
incoming client connections.  The OnGetThread event is triggered in the
context of that worker thread.  If the OnGetThread event handler throws an
uncaught exception, the listening thread will be aborted prematurely.

On the other hand, if the OnGetThread event handler does not return a valid
thread object, TServerSocket will simply create its own thread object
internally.  Which means that not only will the thread not invoke your
processing code at all, but also if the original exception was related to
memory problems then the TServerSocket creating its own thread object could
potentially run into the same problem and throw another exception anyway.

> Should I place Enter/Leave/CriticalSection here or in
> the body of AddRequestToSharedStringList()

A critical section should be locked for as short a time as possible,
wrapping only the code that actually needs protecting.  It is best to place
it inside of AddRequestToSharedStringList() in the place(s) where the
TStringList is actually being accessed.

> Can I trust to '__finally' or it's better to use RAII?

In the code sample you have shown, it does not matter because your use of a
__finally is useless.  You placed it outside of the exception handler, in a
place where code is already guaranteed to run regardess of whether an
exception occurs, so the __finally is redundant.  For a __finally to be
effectively, it should be used before the exception is caught, ie:

    void __fastcall TEmailServerThread::AddRequestToSharedStringList( /*
ARGS */ )
    {
        try
        {
            EnterCriticalSection( &EmlSrvCriticalSection );
            try
            {
                MyStringList->Add ( /* ARGS */ );
            }
            __finally
            {
                LeaveCriticalSection( &EmlSrvCriticalSection );
            }
        }
        catch ( const Exception &e )
        {
            // ...
        }
    }

On the other hand, there is nothing wrong with using RAII, if you do not
want to use Borland-specific compiler extensions:

    struct CritSecLock
    {
        LPCRITICALSECTION fcs;
        CritSecLock(LPCRITICALSECTION acs) : fcs(acs) {
EnterCriticalSection(fcs) };
        ~CritSecLock() { LeaveCriticalSection(fcs) };
    };

    void __fastcall TEmailServerThread::AddRequestToSharedStringList( /*
ARGS */ )
    {
        try
        {
            CritSecLock lock(&EmlSrvCriticalSection);
            MyStringList->Add ( /* ARGS */ );
        }
        catch ( const Exception &e )
        {
            // ...
        }
    }

> Where should I place the sending code?

That depends on where you need to send your data from, and also what kind of
commuication protocol you are trying to implement in the first place.

>       ServiceThread->ProcessRequests( false );

Get rid of that. Your socket threads have no business calling that at all.

> In the code above, I send all the list within a CriticalSection.
> Is it smarter to spawn the CriticalSection in the body of
> SendEmailsFromList() and spaw it for *every* single item to be sent,
> and so leave ServerSocketGetThread() to 'breathe' ?

That depends on whether you need to add new items to the list at the same
time that you are sending them, or if the sending is turning out to be too
much of a bottleneck for you.  Only you can answer that question for
yourself.


Gambit
Re: Simple e-mail server
#3385
Author: Vladimir Stefanovic
Date: Mon, 26 Sep 2005 18:13
29 lines
1705 bytes
> The service will not be shut down.  However, the
> TServerSocket will not function properly anymore. [...]
> If the OnGetThread event handler throws an uncaught
> exception, the listening thread will be aborted prematurely.

Does this happens often in practice? If my TServerSocket
gets in "Active == false" state that means all my server
functionality is dead.

What do you suggest me to get it working again? Maybe
a TTimer which will periodically check the state?

Also is it normal that I make a *second* service that will
only check if the *main* service is STOPPED and decide if
to start it again...

> For a __finally to be effectively, it should be used before
> the exception is caught, ie:
> [CODE]

OK.

Thank you.




Best regards,
Vladimir Stefanovic 
Re: Simple e-mail server
#3386
Author: Remy Lebeau (TeamB)
Date: Mon, 26 Sep 2005 21:54
24 lines
1964 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:433839f8@newsgroups.borland.com...

> If my TServerSocket gets in "Active == false" state
> that means all my server functionality is dead.

The Active property will not be set to false if an uncaught exception is
thrown by the OnGetThread event handler.  Nor will any existing client
connections be closed, either.  However, because the listening thread is
aborted prematurely, no more incoming client connections will be accepted
until the TServerSocke is reset.

> Also is it normal that I make a *second* service that will
> only check if the *main* service is STOPPED and decide
> if to start it again...

What you are describing is known as a "watchdog" service.  Some people
choose to make them, if their main service must remain running under all
cercumstances.  But they are certainly not a requirement for service
programming in general.


Gambit
Re: Simple e-mail server
#3387
Author: Jonathan Benedicto
Date: Mon, 26 Sep 2005 22:25
8 lines
1367 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message 
news:4337dfe1$1@newsgroups.borland.com...
> I would like to make a *simple* e-mail server

I know you must have thought of this, but what about using the Indy POP3 
and SMTP server components ?

Jonathan 
Re: Simple e-mail server
#3389
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:26
29 lines
1862 bytes
No, I am not familiar with Indy. I'll look at the
components you mentiond anyway, but my final goal
is not to make only e-mail server. One day that
could be GSM/GPRS/SMS server or something similar.

I just want to learn how to make a RELIABLE
server with BCB. That means the VALID listening
TServerSocket and STABLE service.

One of the services I alredy made works 4 years
till today with no problems (on Win NT4), but it doesn't
have sockets involved.



-- 
Best regards,
Vladimir Stefanovic
"Jonathan Benedicto" <incorrect@no.server> wrote in message 
news:433874d5@newsgroups.borland.com...
> "Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message 
> news:4337dfe1$1@newsgroups.borland.com...
>> I would like to make a *simple* e-mail server
>
> I know you must have thought of this, but what about using the Indy POP3 
> and SMTP server components ?
>
> Jonathan
> 
Re: Simple e-mail server
#3388
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:35
33 lines
1995 bytes
> However, because the listening thread is
> aborted prematurely, no more incoming client connections
> will be accepted until the TServerSocke is reset.

Remy, thanks for detailed explanations. I'll reread every
word you've written more times, but I still have some more
concers (which are probably result of my bad understanding
of english)

Does that mean, that if exception occure in OnGetThread:

1) that thread is (of course) dead
2) pending threads will be valid and allowed to finish
3) no more client requests are possible untill TServerSocket
reseting.

What is TServerSocket resetting? Is that just Open()ing
again?

In general I want to go home and to be sure that my
TServerSocket is listening and that can survive any
problem that can possibly be involved.

Crach of the TServerSocket because of some reason
is OK, but I want it to restart as soon as possible by
TServiceApplication.




-- 
Best regards,
Vladimir Stefanovic
Re: Simple e-mail server
#3390
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:49
43 lines
2196 bytes
> A critical section should be locked for as short a time as
> possible, wrapping only the code that actually needs
> protecting.

Remy,

If the only job that my TServerSocket::OnGetThread
is to write a row (record) of data to some DB, can
that be considered fast (I expect up to 10 connections
at a time in the worst case)?

From my experience Insert()ing a row of data to a DB
is quite fast.

I ask that because of this:

You tought me how to receive data from server and send
some confirmation back directly from OnGetThread() to
client and that works fine for me.

But what if the operation in the OnGetThread() is not so
fast and I must have Critical Section because of accessing
the same files...

Do you think that I can because of fast escaping from
OnGetThread *only* store the *return* clients' addresses
and later use TClientSocket to send them confirmations
after processing?
Is that even possible because I have to pass the router?

To be more precise can I store ClientSocket in some list
and use it as a return address for confirmation?

void __fastcall TLanService::ServerSocketGetThread(TObject *Sender,
      TServerClientWinSocket *ClientSocket, < ---- STORE THIS
      TServerClientThread *&SocketThread)
{



-- 
Best regards,
Vladimir Stefanovic
Re: Simple e-mail server
#3391
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 17:04
44 lines
2293 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:4339666a@newsgroups.borland.com...

> Does that mean, that if exception occure in OnGetThread:
>
> 1) that thread is (of course) dead

Correct.

> 2) pending threads will be valid and allowed to finish

Yes.  They are already running.  They are not effected by the listening
thread.

> 3) no more client requests are possible untill TServerSocket
> reseting.

Correct.

> What is TServerSocket resetting? Is that just Open()ing
> again?

If the listening thread is terminated, the TServerSocket is still "open".
Closing and re-opening the TServerSocket is the only way to restart the
listening thread.

> In general I want to go home and to be sure that my
> TServerSocket is listening and that can survive any
> problem that can possibly be involved.

TServerSocket can't handle every possible thing that can go wrong.

> Crach of the TServerSocket because of some reason
> is OK, but I want it to restart as soon as possible by
> TServiceApplication.

In this particular situation, there is no way to be notified when the
listening thread has crashed.  You have no access to it at all.  The only
way to know if the server is no longer listening is to actually connect
another client to the server and see if it fails.


Gambit
Re: Simple e-mail server
#3392
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 17:06
19 lines
1682 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:43396990@newsgroups.borland.com...

> If the only job that my TServerSocket::OnGetThread
> is to write a row (record) of data to some DB, can
> that be considered fast (I expect up to 10 connections
> at a time in the worst case)?

That is dependant on the speed of the DB, and the speed of the protection
that you use for guarding the DB from threading issues.

Why are you writing to the DB in the OnGetThread event handler, though?
That is a very odd place to do it.  Or do you mean that your DB code is in
the thread that is started by OnGetThread and is not inside of OnGetThread
itself?


Gambit
Re: Simple e-mail server
#3393
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 18:14
36 lines
1948 bytes
> If the listening thread is terminated, the TServerSocket
> is still "open". Closing and re-opening the TServerSocket
> is the only way to restart the listening thread.

This explanes one of my earlier 'problems'.

>> Crach of the TServerSocket because of some reason
>> is OK, but I want it to restart as soon as possible by
>> TServiceApplication.
>
> In this particular situation, there is no way to be notified
> when the listening thread has crashed.  You have no access
> to it at all.The only way to know if the server is no longer
> listening is to actually connect another client to the server
>and see if it fails.

But can I have a TTimer which periodically checks:

   if ( !ServerSocket1->Active )
   {
      // ...
   }
... or place that code in TServiceApplication::OnExecute()

   while ( !Terminated )
   {
      // ...
   }
?




-- 
Best regards,
Vladimir Stefanovic
Re: Simple e-mail server
#3394
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 18:54
74 lines
2974 bytes
> That is dependant on the speed of the DB,

Inserting new record in MS ACCESS (*.mdb) with
TADOTable is prety fast from my experience.

> and the speed of the protection that you use for
> guarding the DB from threading issues.

The only protection I know for are CriticalSections
(which I learned from you).

// PSEUDO !!!
bool AddNewRecord()
{
   RAII_LOCK();

   DB->Open();
   DB->TABLE->InsertNewRow(DATA);
   DB->Close();
}


> Why are you writing to the DB in the OnGetThread event
> handler, though? That is a very odd place to do it.  Or do
> you mean that your DB code is in the thread that is started
> by OnGetThread and is not inside of OnGetThread itself?

This is what I ment / I shortened the code as far as I could /

void __fastcall TAccountsService::ServerSocketGetThread(TObject *Sender,
      TServerClientWinSocket *ClientSocket,
      TServerClientThread *&SocketThread)
{
   SocketThread = new TAccountsServerThread( false, ClientSocket );
}
//---------------------------------------------------------------------------

void __fastcall TAccountsServerThread::ClientExecute()
{
   TMemoryStream *InboxMemoryStream = new TMemoryStream;
   TWinSocketStream *Stream = new TWinSocketStream ( ClientSocket, 5000 );

   try
   {
      while ( ( !Terminated ) && ( ClientSocket->Connected ) )
      {
         if ( Stream->WaitForData ( 5000 ) )
         {
            if( !ReadData ( Stream, InboxMemoryStream, 1000 ) )
            {
               ClientSocket->Close();
               break;
            }

            // SAVING TO DB CODE (PSEUDO, SHOWN ABOVE)
           bool Result = AddNewRecord();

            SendingNotificationToClientIfTheTaskWasSuccessfulOrNot( Stream, 
InboxMemoryStream, Result );
         }
      }
      // From here connection is closed ...
   }
   __finally
   {
      delete Stream;
      delete InboxMemoryStream;
   }
}


-- 
Best regards,
Vladimir Stefanovic 
Re: Simple e-mail server
#3396
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 19:23
26 lines
1745 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:43398ba5@newsgroups.borland.com...

> But can I have a TTimer which periodically checks:
>
>    if ( !ServerSocket1->Active )

As I already told you earlier, the server is not closed when the listening
thread crashes.  The server is still active and running, it is just not
accepting any further incoming connections.  The Active property remains set
to 'true'.

> ... or place that code in TServiceApplication::OnExecute()
>
>    while ( !Terminated )
>    {
>       // ...
>    }
> ?

That checks whether the service itself has been terminated, which has
nothing to do with the TServerSocket.


Gambit
Re: Simple e-mail server
#3395
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 19:26
20 lines
1648 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:43399502@newsgroups.borland.com...

> The only protection I know for are CriticalSections

A critical section is just one of many ways to provide thread safety to
code.

>    DB->Open();
>    DB->TABLE->InsertNewRow(DATA);
>    DB->Close();

Why are you re-opening and closing the database each time you insert a new
record?  Why not leave the DB open while the service is running?  Or, if
each client is getting its own connection to the DB, then open the DB when
the client connects and then close it when the client disconnects.


Gambit
Re: Simple e-mail server
#3397
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 19:42
29 lines
1740 bytes
>>    DB->Open();
>>    DB->TABLE->InsertNewRow(DATA);
>>    DB->Close();
>
> Why are you re-opening and closing the database
> each time you insert a new record?  Why not leave
> the DB open while the service is running?

OK. That's how would I do that in practice anyway :).

Thanks.

P.S. Once, when I Googled to find your web site
because of some components (with code) you have,
I noticed many, many, many web sites with keywords:
Remy LeBeau, Gambit, X-Man, X-Man uniforms, drawings,
some dialogs... etc ... and similar.

Then I found on your site also some drawings...

Are you somehow connected with all that or it's just
coincidence?




-- 
Best regards,
Vladimir Stefanovic 
Re: Simple e-mail server
#3398
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 19:52
17 lines
1625 bytes
> As I already told you earlier, the server is not
> closed when the listening thread crashes.  The server
> is still active and running, it is just not accepting any further
> incoming connections.  The Active property remains set
> to 'true'.

Now, I will definitely implement 'watchdog' service:

1) to monitor if the server was sudenly shuted-down
2) to send periodically fake requests to server to see
if it's responding



-- 
Best regards,
Vladimir Stefanovic
Re: Simple e-mail server
#3400
Author: Jonathan Benedicto
Date: Tue, 27 Sep 2005 21:39
19 lines
1704 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message 
news:4339644b@newsgroups.borland.com...
> No, I am not familiar with Indy. I'll look at the
> components you mentiond anyway, but my final goal
> is not to make only e-mail server. One day that
> could be GSM/GPRS/SMS server or something similar.

Oh I see.

> I just want to learn how to make a RELIABLE
> server with BCB. That means the VALID listening
> TServerSocket and STABLE service.

You should enjoy the learning experience. You might want to use the Indy 
tcp server socket component instead though. It automatically handles the 
threads for you. I prefer Indy to the TServer/ClientSocket components, 
except where I want a single threaded server.

Jonathan 
Re: Simple e-mail server
#3399
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 21:45
15 lines
1798 bytes
"Vladimir Stefanovic" <antivari@po.sbb.co.yu> wrote in message
news:4339a2a5@newsgroups.borland.com...

> 2) to send periodically fake requests to server to
> see if it's responding

Keep in mind that if the server has a lot of simultaneous pending
connections at a time, it may refuse any new connections until the list of
pending connections has been cleared out.  The listening thread is still
running fine during this situation.  Your watchdog fails to connect, it may
want to try connecting a few more times before finally deciding to give up.


Gambit
Re: Simple e-mail server
#3401
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 21:46
11 lines
1282 bytes
"Jonathan Benedicto" <incorrect@no.server> wrote in message
news:4339bb8e$1@newsgroups.borland.com...

> I prefer Indy to the TServer/ClientSocket components, except
> where I want a single threaded server.

Indy has a TSimpleServer component that can be used in that situation.


Gambit
Re: Simple e-mail server
#3402
Author: Jonathan Benedicto
Date: Tue, 27 Sep 2005 21:52
7 lines
1418 bytes
"Remy Lebeau (TeamB)" <no.spam@no.spam.com> wrote in message 
news:4339bd78@newsgroups.borland.com...
> Indy has a TSimpleServer component that can be used in that situation.

Thank you for that info. I'll find it useful.

Jonathan 
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads