Click here to Skip to main content
Click here to Skip to main content

CSPServer, State-based Protocol Server Class

By , 11 Mar 2003
 

Introduction

CSPServer is a VC++ class which makes it relatively easy to create solid, multi-threaded client/server state-based protocol servers (SPS).  Common existing standard client/server SPS systems are SMTP, POP3, FTP, NNTP and others systems which millions of people use every day on the internet. 

CSPServer gives you a time-tested, well engineered framework to create your own standard protocol server or a new protocol server that suits your needs.  CSPServer is used in Santronics Software's intranet hosting product, Wildcat! Interactive Net Server (http://www.santronics.com) to provide an  integrated multiple protocol intranet hosting system.   Proprietary virtual communications technology was removed to make a public socket-based only version of CSPServer

This article will explain how to use the CSPServer class with working SPS examples.  This is the author's first CodeProject article subsmission, so all commentators and critics are welcome.

Background

A State-based Protocol Server or SPS is client/server methodology where by a client application connects to a server application to begin a text based "controlled" conversation.  This controlled conversation is often called the "State Machine."  

In a state machine, a connected client will issue a command and then wait for a server response to the command.  In a properly designed state machine, the client can not continue with additional commands until a response was provided by the server for the current command.  It is very important to understand that all conversations in a state machine begins with the client issuing commands. The server will never send data or information to the client unless it was requested or in response to a client command. 

CSPServer offers a framework to create your own client/server state machine conversation for your client/server application. 

If you understand this basic concept, you can skip the next background section which illustrates a SPS using a standard SMTP server.

Example standard SPS - SMTP

If you ever connected to standard SPS such as SMTP, POP3, FTP, NNTP etc, the first line you see is the welcome line.   The best way to see this is to use a standard TELNET client such as the one that comes with Windows.  For example, to connect to the Microsoft SMTP server (port 25) using telnet, type the following:

Telnet maila.microsoft.com 25

If successful, you will see the welcome line.  You can type HELP to see the available commands.  Most standard SPS systems will provide HELP information on the available commands.

However, SPS systems are ultimately designed for automated applications, not human interaction.  Client software are used to automate the process, such as sending an email.   The following illustrates what typically happens when you want to send an email to anyone in the world.

Example SMTP client/server session:

Lets assume the target address for the email is gbush@whitehouse.gov  and lets assume you are using Outlook Express (OE) to create and send the email.  OE has a built-in SMTP client component which is used to send mail to a SMTP server. 

The following are the steps taken to send the email.

  1. OE smtp client obtains the MX record for the domain whitehouse.gov. The MX provides the IP address location of the SMTP server. The client will then connect to the IP address defined by the MX record.
  2. The client waits for the welcome response and then issues the HELO or EHLO command.    The client waits for a positive response.
  3. The client issues the command MAIL FROM: <youraddress> and waits for a positive response. 
  4. The client issues the command RCPT TO: <gbush@whitehouse.gov> and waits for a positive response.  A negative response means the address is invalid or some other error, like mailbox is full.
  5. The client issue the DATA: command and waits for a positive response.
  6. The client begins to send the actual email message line by line, ending it with a "." line.  The client then waits for a positive response indicating the email was successfully received.
  7. The client issues the QUIT command and waits for a positive response.
  8. The client ends.

In summary, the smtp client commands and smtp server responses occur:

SMTP CLIENT COMMANDS SMTP SERVER RESPONSES
  220 Connected to Domain XXX,  Server Ready!
HELO or EHLO <client domain name> 250 Hello Client Domain!
MAIL FROM: <your email address> 250 <address>.... Sender Ok!
RCPT TO: <gbush@whitehouse.gov> 250 <address>.... Receipient ok
DATA 354 Start mail input; end with <CRLF>.<CRLF>
email message  
. 250 Message received!
QUIT 221 Closing connection, Goodbye!

Please note how the SMTP server uses numeric response codes for server responses.   They control how the client will react.  For example, when the client issues the RCPT TO: command,  the positive response code is 250 to indicate the address is acceptable.  However, negative response codes such as 550 can be issued which means the "unknown address."

The point behind this example is to illustrate the "tight" client/server state machine conversation between a state-based protocol server and a state-based protocol client such as in SMTP. Servers like SMTP have specific RFC design guidelines that describe the proper state machine (commands and responses).  The same is true for FTP, NNTP and POP3.

With CSPServer, you can create your own client/server state machine conversation.  You can use a similar response code concept for your own for your particular client/server application.

Understanding the CSPServer State Machine

The following figure 1.0 illustrates the "state machine" in CSPServer:

Figure 1.0
CSPServer State machine

Server Applet
Client Connection Listening Thread

<--connect--

Client Applet

|
Accept
|

Instantiate subclass CSPServer
Object thread

|

Call subclass
Go() Handler

|

call subclass SendWelcome()handler

--response-->

Client waits for welcome response

Command1()handler

<-----------

command1

--response-->

Command2()handler

<-----------

command2

--response-->

.
.

.
.

CommandN()handler

<-----------

commandN

--response-->

run optional subclass Cleanup()handler when client disconnects

When the client first connects , the listening server will start a new CSPServer session which start a new thread to manage client session.  The thread handler will call the subclass Go() handler.

The subclass Go() handler can be used to collect connection information but its main goal is to start the state machine engine by calling the inherited Go() handler.

The inherited Go() handler will then call the virtual "SendWelcome()" function and begin the state machine.  The SendWelcome() override provides the opportunity for the SPS to  introduce itself  and also possibly supply "readiness" information to the client.

Using the CSPServer Class

For those who wish to get started quickly,  the following is a "quick how to use" outline.  For technical class or code details see the source code and examples provided.

At a minimum, to create a SPS using the CSPServer class, you need to do following items (in no particular order):

  1. Create a subclass of CSPServer,
  2. Override the subclass constructor,
  3. Override of the Go() handler in your subclass,
  4. Add a TSPDispatch member variable,
  5. Create a TSPDispatch structure defining the state machine dispatch commands,
  6. Add command dispatch handlers to the subclass, and
  7. Create a Listening Server Thread to answer incoming connections

There are other virtual functions you can override, but the constructor and Go() are the only required overrides to start the CSPServer engine.

The SampleServer.cpp source file contains a complete working example of a SPS.   The following are the basic steps in create an SPS:

Step 1:

Add #include <spserver.h> to your source code, and create a CSPServer subclass (i.e., CMySPServer) such as the one shown below. 

For the sake of an example, we will create a state machine with 5 commands;   "HELLO", "LOGIN", "SHOW", "HELP" and "QUIT".  So for each command a handler is added.

#include <spserver.h>

class CMySPServer: public CSPServer {
    typedef CSPServer inherited;
public:
    CMySPServer(CSocketIO *s);  // REQUIRED
protected:
    virtual void Go(); // REQUIRED
    virtual void SendWelcome();
private:
    static TSPDispatch Dispatch[]; // REQUIRED
    BOOL SPD_HELLO(char *args);
    BOOL SPD_LOGIN(char *args);
    BOOL SPD_SHOW(char *args);
    BOOL SPD_HELP(char *args);
    BOOL SPD_QUIT(char *args);
};

Please note the SendWelcome() override is optional.  However, it is almost always required to send a connection response to the client when the client first connects to the server. 

The class CSocketIO is a simple socket wrapper with formatting functions and a socket input circular buffer.  This class documentation is not within the scope of this article.  See the source file socketio.h/cpp for usage and reference.

Step 2:

Create the TSPDispatch structure for the subclass member Dispatch declaring the commands and the commands dispatch handles as follows

CSPServer::TSPDispatch CMySPServer::Dispatch[] = {
  SPCMD(CMySPServer, "HELLO", SPD_HELLO),
  SPCMD(CMySPServer, "LOGIN", SPD_LOGIN),
  SPCMD(CMySPServer, "SHOW",  SPD_SHOW),
  SPCMD(CMySPServer, "HELP",  SPD_HELP),
  SPCMD(CMySPServer, "QUIT",  SPD_QUIT),
  {0}
};

For each command in the Dispatch structure, declare a dispatch handler in the subclass using the following prototype:

BOOL dispatch_handler_name(char *args);

Advanced Usage: It is possible to have an single handler for call commands.  In this case, you can use the method GetCurrentCommandName() to return the current command issued.

Step 3:

Now begin to add the implementation of the overrides and the dispatch handlers:

//////////////////////////////////////////////////////////////
// Constructor

CMySPServer::CMySPServer(CSocketIO *s)
     : CSPServer(s, Dispatch)
{
    // Initialize all your session variables here.

    // Done is a special BOOL used to exit
    // the inherited::Go() handler. One of the
    // Dispatch commands should set Done = TRUE;
    // i.e., QUIT() command.

    Done = FALSE;

    // start thread, calls Go() handler. If you
    // wish, you can call Start() outside the constructor.

    Start();  
}

//////////////////////////////////////////////////////////////
// Go() is called by start()

void CMySPServer::Go()
{
    // By this point, we have a new thread running. 
    // This is a good point to collect client ip or
    // domain information.

    // To start the state machine, you must call
    // the inherited Go() function.  This will
    // starts the thread's socket command line
    // reader and dispatcher. Go() returns when 
    // the Done is set TRUE or if connection drops
    // or one of the dispatch handlers return FALSE.

    inherited::Go(); // REQUIRED

    // we are done, good place to do session
    // cleanup.

    delete this;  // REQUIRED
}

//////////////////////////////////////////////////////////////
// SendWelcome() is called by the inherited Go(). This is 
// a good place to provide "server readiness" information
// to the connecting client.  Standard SPS use numeric 
// response codes to provide this information. 

void CMySPServer::SendWelcome()
{
    Send("Hello! Server ready\r\n");
}

//////////////////////////////////////////////////////////////
// Dispatch handlers.  
//
// Dispatch handlers have one parameter, char *args. It will 
// contain the string, if any, passed with the command. 
// 
// Return TRUE to continue the state machine. If FALSE is 
// returned, the session ends. NOTE: Returning FALSE is not 
// a good idea in practical designs as it can put the remote 
// client in a irregular state. You should always have a 
// graceful way to complete a session.  Even if you wish to 
// show "error" conditions, you should always return TRUE.

BOOL CMySPServer::SPD_HELLO(char *args)
{
    Send("--> HELLO(%s)\r\n",args);
    return TRUE;
}

BOOL CMySPServer::SPD_LOGIN(char *args)
{
    Send("--> LOGIN(%s)\r\n",args);
    return TRUE;
}

BOOL CMySPServer::SPD_SHOW(char *args)
{
    Send("--> SHOW(%s)\r\n",args);
    return TRUE;
}

BOOL CMySPServer::SPD_HELP(char *args)
{
    Send("--- HELP commands ---\r\n");
    Send("HELLO\r\n");
    Send("LOGIN\r\n");
    Send("SHOW\r\n");
    Send("HELP\r\n");
    Send("QUIT\r\n");
    Send("--- end of help ---\r\n");
    return TRUE;
}

BOOL CMySPServer::SPD_QUIT(char *)
{
    Send("<CLICK> Bye!\r\n");
    Control->Shutdown();   // Disconnects socket
    Done = TRUE;           // Tells Go() to exit
    return TRUE;
}

Step 4:

Finally, now that you have a CSPServer class ready, you need a listening server thread that will answer incoming socket connections and for each new connection, a CSPServer instance is started. 

To create the Listening Server, the CThread class is used:

class CServerThread : public CThread {
    typedef CThread inherited;
public:
   CServerThread(const DWORD port = 4044, const DWORD flags = 0);
   virtual void Stop();
protected:
   virtual void Go();
private:
   SOCKET serverSock;
   DWORD serverPort;
};

The subclass Go() handler is used to create the listening socket server. 

When a new connection is accepted, a new instance of CMySPServer is created passing the peer socket handle as a new  CSocketIO object in the CMySPServer constructor.  The following is done in the CServerThread::Go() handler:

  .
  .
  SOCKET t = accept(serverSock, (sockaddr *)&src, &x);
  if (serverSock == INVALID_SOCKET) break; // listening server broken
  new CMySPServer(new CSocketIO(t));       // Start new CMySPServer session
  .
  . 

You don't need to work about releasing the objects.  The class themselves will do the cleanup.

Example usage of CServerThread in a console application:

  CServerThread server(4044);

  while (!Abort) {
    if (kbhit() && getch() == 27) break;  // Escape to Exit
    Sleep(30);
  }

  server.Stop();

Points of Interest

See the source file SampleServer.cpp for a complete working example.  By default, the example uses port 4044.   To test the server, use telnet like so:

    Telnet LocalHost 4044

History

  • v1.0P - March 4, 2003, Initial Public Release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

hector santos
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionInterested in updated version?memberhector santos2 Aug '10 - 20:09 
Its been nearly 7 years since I posted this project. Many people liked it and I was very happy that he help them in their own implementations.
 
Its good stable code but we enchanced it slightly, its compiles under all the Visual Studio which caught some C++ syntax issues.
 
I wish to know if there is interested in posting the current version or if you like me, "don't change what isn't broke", never mind. Smile | :)
 
Hector
AnswerRe: Interested in updated version?memberMember 223095418 Sep '10 - 22:40 
Sure, I'd like it.Come on, just show me the updated version!!! Big Grin | :-D Big Grin | :-D Big Grin | :-D
GeneralRe: Interested in updated version?memberhector santos31 Dec '10 - 8:19 
Smile | :)
 
Actually, for most of 2010, I have doing a lot of R&D with a I/O Completion Port (IOCP) model, concentrating on the separation of IOCP modeling task to see what modifications to the CPServer and related classes need to be made to offer a IOCP version.
 
First, I learned how IOCP worked to see all the relevant parts and wow, there is no doubt, IOCP is more efficient allowing you to handle much higher set of client connections. Then I began to see what I needed to modify in CSPServer and CSocketIO. But my view was still "green" from an IOCP point of view and it appeared to a substantial change to the classes would be needed. So I had put it to the back burner around June 2010.
 
But back in November 2010, a customer began to report memory leaks with our SMTP server and I found that the self-delete CSPServer class was intermittently creating a handle leak under MULTI-CORE computers. The higher the connection rate, the more you saw it. But you can also see it with just one connection on a dual-core computer. This was confirmed by me and another programer with a simple C++ self-delete class and some MVPs at MSDN Forums have suggested that report and call Microsoft about this. This was after they suggested to try UMDH, a "Debugging Tools for Windows" heap analysis tool:
 
Umdhtools.exe: How to use Umdh.exe to find memory leaks[^]
 
But digging deaper with the UMDH , what I have found is that the Microsoft was creating leaks by wrappering Winsock 1.1 application calls to internal Winsock 2.0 interface and in fact, there were many reports of people seeing leaks with just simple socket applications. MS has an article that explain this and pretty say use IOCP and worker pools to reduce the leak issues.
 
So I began that effort over the holidays and now that I know how to use IOCP, today I am going explore a slightly modified CSPServer version with IOCP support. If its successful, I will see I have the time in the new year to update CSPServer to a IOCP version.
 
Thanks Smile | :)
GeneralCompiler error C4867memberRan Katzman1 Dec '06 - 7:51 
Thank you for the class Hector, very useful.
 
Compiling it on VS 2005 you get errors C4867 for entries defined in the TSPDispatch structure.
 
To fix the errors and make the code compliant with Standard C++:
1) In SPServer.h, replace this macro -
#define SPCMD(cls, cmd, func) \
{cmd, (BOOL (CSPServer::*)(char *))(cls::func)}
With this one -
#define SPCMD(cmd, func) \
{cmd, (BOOL (CSPServer::*)(char *))(func)}

 
2) In your TSPDispatch struct, replace entries such as these -
SPCMD(CMySPServer, "HELLO", SPD_HELLO),
With these -
SPCMD("HELLO", &CMySPServer::SPD_HELLO),
 
Ran
GeneralRe: Compiler error C4867memberhector santos3 Dec '06 - 23:23 
Thanks for the feedback. We just started to begin the migration to VS 2005 so we probably would of caught this at some point. Smile | :)
 
Thanks again and enjoy the class.
 
---
HLS
 

Generalredundant check conditions make the code harder to understandmemberehaerim9 Jul '06 - 11:03 
Dear Hector Santos,

CSPServer is one of the most valuable article in the codeproject.

Below is excerpted from the source.

I think the two checking conditions (bold ones) identical except that CheckAbort checks "Done" variable first. I think both are effectively identical so that I can remove redundant one. It's really hard to understand why you put two "WaitForSingleObject(terminate, 0) != WAIT_OBJECT_0".

DWORD CSocketIO::WaitForReceivedData(DWORD timeoutms, HANDLE terminate)
{
...
 
DWORD start = GetTickCount();
do
{
....
....
} while (((timeoutms == INFINITE) || ((GetTickCount() - start) < timeoutms))
&& (Socket != INVALID_SOCKET) && !CheckAbort()
&& ((terminate == NULL) || (WaitForSingleObject(terminate, 0) != WAIT_OBJECT_0)));

return WAIT_TIMEOUT;
}

Wouldn't it be equivalent to the following code?

DWORD CSocketIO::WaitForReceivedData(DWORD timeoutms, HANDLE terminate)
{
...
 
DWORD start = GetTickCount();
do
{
....
....
} while (((timeoutms == INFINITE) || ((GetTickCount() - start) < timeoutms))
&& (Socket != INVALID_SOCKET) && !CheckAbort());

return WAIT_TIMEOUT;
}


haerim
 
BTW, your email codeproject@winserver.com is not reachable. Please let me know if you have one.
Questionblocking socket vs nonblocking socket and other ways to implement CSPServer?memberehaerim8 Jul '06 - 15:56 
The sockets used in this article is "blocking" socket.
 
I would be happy to see if this CSPServer is implemented using "nonblocking" socket.
 
I wish I could do, but it is beyond my ability yet. Maybe a few years will take me to do it...
 
So, someone please try for the sake of others...
 
LHR
 
Also, it would be great to see this implemented using WSAAsyncSelect, WSASelectEvent, Overlapped, and Completion Port methods. This will be a big challenge worth trying.
QuestionAny client applicaton sample like CSPClient?memberehaerim8 Jul '06 - 9:40 
Hi
 
I am kind of a beginner in socket programming and really enjoyed this article.
 
I really like to apply the State-base Protocol concept in a client socket application. So, it would be really great if someone would post a State-based protocol client sample (I would call it CSPClient) using the same concept in this article.
 
LHR
GeneralSMTP ServermemberPham Tien Thanh20 Jan '06 - 2:09 
Hello!
Thanks for your useful project.
I have just create a SMTP server based on your project
so would you please give me permission to contribute this SMTP server wide open.
Thank you.
GeneralRe: SMTP Servermemberhector santos26 Jan '06 - 23:23 
Thanks for your comment!
 
If I had any expectation or plan to restrict distribution, I would had not publically posted the source code project. Smile | :)
 
My only requirement, which is natural law and standard ethical practice, is to just keep our copyright in all our source code, even if modified and if it makes you feel ok, add credit where it is due in your documentations. i.e., "Portions of this system is copyrighted by Santronics Software, Inc."
 
Thanks

 
Hector Santos, CTO
http://www.santronics.com

GeneralSMTP client questionmemberLior Shoval4 Feb '04 - 6:38 
Hi.
 
Great article.
One question though:
how does an email client sends the following email (for example):
Hello jhon.
I have a surprise. scroll down
.
.
 
.
 
.
.
.
 
.
 

.
 
surprise !!!
(end of email).
 
woudln't the DATA handler stop reading the email after the first newline, ".", lines ?

GeneralRe: SMTP client questionmemberhector santos5 Feb '04 - 1:47 
You double the dots or put a space afterwards.

 
Hector Santos, Santronics
http://www.santronics.com
GeneralRe: SMTP client questionmembergyb99826 Oct '05 - 15:55 
you'd better use base64 to encode the email
and decode it when receive.
By using Base64 your mail can not has
"./r/n" string.;)
 
None Word
GeneralSecurity vulnerabilitymemberPetar K. Shomov2 Dec '03 - 4:18 
Hello,
 
I just spotted this and thought you might want to fix it:
From spserver.cpp Line 54:
 
char cmd[1024];
ZeroMemory(&cmd,sizeof(cmd));
char *s = strchr(cmdline, ' ');
char *args;
 
if (s) {
strncpy(cmd, cmdline, s-cmdline);

 
The strncpy is not safe.
 
Regards,
 
Petar

GeneralCommunication between threadsmemberrromerot16 Oct '03 - 5:52 
If some user is logged in and wants to know who else is logged in, how would I go about doing that. Would the listening thread have to keep track of connections.
 
Any help or suggestions would be appreciated.
 
Ray
GeneralMFC Dependencymemberrromerot2 Jul '03 - 3:20 
Did you ever get the chance to remove the MFC dependency? Do you have a guest program you can share that uses your CSocketIO class?
 
Ray
GeneralRe: MFC Dependencymemberhector santos2 Jul '03 - 11:53 
Yes, I did create a non-mfc version. I just did not get the time to post it here. I'll jot this down for this weekend.

 
hector santos
http://www.santronics.com
GeneralSMTP DATA command problemmemberAlexis MICHEL29 Apr '03 - 8:54 
Sorry to bother you again,
I am trying to do a SMTP server right now.
one of the things I noticed, is that when the client/server communication is at the state of exchanging data (after the DATA command, and until a "." line is found) then there can be anything in the command line.
So far, in my examples, it sent me to the SendCommandError and I could "sort of" (if I was in a DATA state) deal with the data. I always thought there ought to be a more elegant way, but that for the time being it was ok.
In fact it wasn't, if a mail client is sending a mail with one of the line is HELO (typically, the first line with a typo) or whatever that is a recognized command, then it is leading me to the HELO handler, and also the "cmdline" has been stripped of its HELO.
That is a little bit of a problem.
I have tried to think of a way to have it sorted but so far nothing came up.
Typically whenever the SMTP server is in DATA state, it has to accept just about anything and direct it to the same function, until the "." is met.
Would you have a clue ?
 
"Marriage is the triumph of imagination over intelligence. Second marriage is the triumph of hope over experience"
--Oscar Wilde
GeneralRe: SMTP DATA command problemmemberhector santos29 Apr '03 - 13:41 
Alexis, I am at lost of what is the problem.
 
Are you saying that while you collecting information via your DATA handler, another command can come in?
 
If so, that shouldn't happen as the handlers are called synchronously. Another command will not be processed until the handler returns.
 
If you can show me or give me an example (email directly to codeproject@winserver.com) I can then see what you are talking about.
 
We use the internal version of CSPSERVER in our mail server. This private version includes SSL support, amoung other things etc which was pulled for public distribution. Our mail server is used by thousands of customers world wide from low end to high end operations.
 
Anway, send me via email what you are doing and I will take a look at it.
 
Thanks for your interest in CSPSERVER

GeneralRe: SMTP DATA command problemmemberhector santos29 Apr '03 - 16:32 
Ok, I think I know what you are experiecing since the socket class was not well documented.
 
In the DATA handler, you need to read the socket directly using the socket class function:
 
Control->GetBlock();
 
Control is a instance to the current socket class.
 
So your DATA handler() will look like this:
 
BOOL CSmtpServerSession::SPD_DATA(char *args)
{
  Send("354 Start mail input; end with <CRLF>.<CRLF>\r\n");
 
  //----------------------------------------------
  // open a file to receive email data block
  //----------------------------------------------

  HANDLE hData = CreateFile(......);
 
  //
  // this buffer read logic handles the final "." line
  // detection.  
  //

  char buf[4+8192];
  ZeroMemory(&buf,sizeof(buf));
  DWORD totalsize=0;
 
  while (true) {
     int len = Control->GetBlock(&buf[4], sizeof(buf)-4);
     if (len <= 0) {
         // socket disconnected 
         CloseHandle(hData);
         return TRUE;
     }
 
     for (int i = 0; i < len; i++) {
       if (buf[4+i]   =='.' && 
           buf[4+i-1] =='.' && 
           buf[4+i-2] == '\n') {
            MoveMemory(&buf[4+i], &buf[4+i+1], len-(i+1));
            len--;
          }
     }
 
     totalsize += len;
     DWORD dw;
     if (!WriteFile(hData, &buf[4], len, &dw, NULL)) {
        CloseHandle(hData);
        return TRUE;
     }
     if (strncmp(&buf[4+len-5], "\r\n.\r\n", 5) == 0) {
        SetFilePointer(hData, -3, NULL, FILE_END);
        SetEndOfFile(hData);
        break;
     }
     MoveMemory(buf, &buf[len], 4);
  } // while

  CloseHandle(hData);
  Send("250 Message Received (bytes: %d)\r\n",totalsize);
  Return TRUE;
}
Hope this helps! Take a look at the socketio.cpp class. It has all the functions you need to handle socket I/O.
 

 
Hector Santos
http://www.santronics.com
GeneralRe: SMTP DATA command problemmemberAlexis MICHEL30 Apr '03 - 23:29 
This is exactly what I meant, I didn't think you could handle all the data in the DATA handler, I was returning from the DATA handler and catching lines of data with other handlers.
This is going to simplify greatly the work .. Smile | :)
I am sure your server is really good, I didn't mean to imply it had a problem (rather I had a problem ... Smile | :) in fact I think it is an excellent design.
I will look more into the socket class, in the future.
Thanks a lot for your quick help !
 
"Marriage is the triumph of imagination over intelligence. Second marriage is the triumph of hope over experience"
--Oscar Wilde
GeneralAnother questionmemberSalvador Dali16 Apr '03 - 4:57 
What if the server wants to send something to the client without the client initiating the communication first? I mean, the client is connected, but it doesn't send anything.
Correct me if I'm wrong, but the way your classes are designed, the server only replies to the client, right?
GeneralRe: Another questionmemberhector santos16 Apr '03 - 7:30 
In a client/server, one of the basic designs is that the server never starts a conversation the server. The client starts the conversation.
 
So no, it is not possible with this desing to send server data to the client WITHOUT the client asking for it.
 
Read the article, it is discussed there.

GeneralMemory leaks when exiting servermemberSalvador Dali14 Apr '03 - 14:13 
Hello,
 
I was just wondering, I have a few member variables declared in my CMyServer class derived from CSPServer. When I exit my program without shutting down the server and while still having active connections, I get memory leaks. While debugging, I noticed that the CThread destructor is called, but the spserver and CMyServer destructors are not.
 
Am I doing something wrong?
GeneralRe: Memory leaks when exiting servermemberhector santos14 Apr '03 - 19:28 
Well, yes, you will need a graceful shutdown of your listening thread and also probably the active sessions started.
 
Some systems will ignore all this when you "exiting" the application, but if you want it all be clean, then you will need to track the sessions and somehow close them, then close the listening thread.
 
There are many ways to do this. For example, when CMyServer is created (new CmyServer()), you can remember the pointer in some global array. The destructor can then remove its entry from the global array and when you exit the application you check the global array to see if there are any active sessions.
 
But again, it all depends on the applications.
 
You can have a global "TerminateApplication" Event that each session will check to see if its the system is about to shutdown. In our applicaitons, we use the CheckAbort() or the Control->Online() members for this.
 
In any case, this is all subjective. Some might argue that all the extra stuff just so that you can exit is unnecessary, so they ignore it. But others like me, sometimes Wink | ;) , will prefer to have graceful cleanups.
 
---

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 12 Mar 2003
Article Copyright 2003 by hector santos
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid