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
Author: Vladimir Stefanovic
Date: Mon, 26 Sep 2005 11:47
Date: Mon, 26 Sep 2005 11:47
133 lines
4308 bytes
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
Author: Remy Lebeau (TeamB)
Date: Mon, 26 Sep 2005 17:03
Date: Mon, 26 Sep 2005 17:03
105 lines
5040 bytes
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
Author: Vladimir Stefanovic
Date: Mon, 26 Sep 2005 18:13
Date: Mon, 26 Sep 2005 18:13
29 lines
1705 bytes
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
Author: Remy Lebeau (TeamB)
Date: Mon, 26 Sep 2005 21:54
Date: Mon, 26 Sep 2005 21:54
24 lines
1964 bytes
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
Author: Jonathan Benedicto
Date: Mon, 26 Sep 2005 22:25
Date: Mon, 26 Sep 2005 22:25
8 lines
1367 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:26
Date: Tue, 27 Sep 2005 15:26
29 lines
1862 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:35
Date: Tue, 27 Sep 2005 15:35
33 lines
1995 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 15:49
Date: Tue, 27 Sep 2005 15:49
43 lines
2196 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 17:04
Date: Tue, 27 Sep 2005 17:04
44 lines
2293 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 17:06
Date: Tue, 27 Sep 2005 17:06
19 lines
1682 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 18:14
Date: Tue, 27 Sep 2005 18:14
36 lines
1948 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 18:54
Date: Tue, 27 Sep 2005 18:54
74 lines
2974 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 19:23
Date: Tue, 27 Sep 2005 19:23
26 lines
1745 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 19:26
Date: Tue, 27 Sep 2005 19:26
20 lines
1648 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 19:42
Date: Tue, 27 Sep 2005 19:42
29 lines
1740 bytes
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
Author: Vladimir Stefanovic
Date: Tue, 27 Sep 2005 19:52
Date: Tue, 27 Sep 2005 19:52
17 lines
1625 bytes
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
Author: Jonathan Benedicto
Date: Tue, 27 Sep 2005 21:39
Date: Tue, 27 Sep 2005 21:39
19 lines
1704 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 21:45
Date: Tue, 27 Sep 2005 21:45
15 lines
1798 bytes
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
Author: Remy Lebeau (TeamB)
Date: Tue, 27 Sep 2005 21:46
Date: Tue, 27 Sep 2005 21:46
11 lines
1282 bytes
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
Author: Jonathan Benedicto
Date: Tue, 27 Sep 2005 21:52
Date: Tue, 27 Sep 2005 21:52
7 lines
1418 bytes
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