paradigm_3/html/
/*! \mainpage Paradigm
 \image html logo.gif

 \section intro Introduction
  The TCP/IP network progamming examples I've seen generally ignore
  error detection and handling.  Unfortunately this often results
  in poor quality servers being releasing by those who learn by example.
  This program features exhaustive error detection and handling.
  Memory leaks and buffer overruns are also ubiquitous in TCP/IP servers.
  This example attempts to mitigate those issues.  At least that's the
  hope.

 \section desc Description
  Paradigm in its current state is a very primitive chat server written
  in C++ for Win32 systems supporting Winsock2.  It allows multiple
  connections and just echoes input to all those connected.

 \section feat Features
  - Proper error detection.
  - Dynamic buffers.
  - Lots of comments and formatted documentation using Doxygen QT style
    notation.
  - No memory leaks.  Tested with MemProof.
  - Multithreaded - Network server and Chat driver run in their own threads.

 \section impl Implementation
  A non-blocking select-based server model was selected for this
  illustration for no particular reason other than usefulness of the
  example to other BSD derived TCP/IP stack implementations.<p>
  There are no actual winsock2 dependencies in this version despite
  the negotiation present for winsock2.  It should work on winsock1
  systems by modifiying the WSAStartup negotiation and including the
  appropriate headers.

 \section changes Changes
  - version 0.30
    - Separation of Server from main program.  The Server now runs in it's
      own thread.
    - Communication between the threads is handled by create and placing
      an Event object on shared EventQueue's.
    - EventQueue class wraps queues with thread safe guards.
    - The primitive notion of a User class added.

  - version 0.22
    - A simple logging mechanism added via class Log.

  - version 0.20
    - Refactored all of the low level sockets routines in Connection
      to a wrapper class Socket.
    - Socket errors are implemented as exceptions.
    - Implemented a circular autoflushing buffer in the Socket class.
    - Communication between the server and client is handled by create and placing
      Event objects on queues.
    - Additonal comments in Server class.
    - Added Microsoft Visual C++ project

  - version 0.10
    - Initial version

 \section depend System Dependencies
  This version only works on Windows 32-bit operating systems and was
  tested with Windows98, Windows NT 4.0 and Windows XP.
  This version was compiled and tested with the following compilers:
  - Borland 5.3   (Borland Builder 3.0)
  - Borland 5.5.1 (Borland's free commandline compiler)
  - Microsoft Visual C++ 6.0

 \section install Installation
   \subsection bb3 Borland Builder 3.0 IDE
     -# Unzip the distribution into the folders it wants.
     -# Start Builder and open the project group (paradigm_3.bpg)
        in the paradigm directory.
     -# Click build
     -# Open a Windows command console window and switch to the paradigm
        directory by typing: <b>cd c:\\paradigm</b> or to the path
        you placed it.
     -# Run the server by typing: <b>paradigm</b><p>

   \subsection bc55 Borland's Free Command Line Compiler
     -# Unzip the distribution into the folders it wants.
     -# Open a Windows command console window and switch to the paradigm
        directory by typing: <b>cd c:\\paradigm</b> or to the path
        you placed it.
     -# Then type: <b>make</b> and watch it compile.
     -# Run the server by typing: <b>paradigm</b>

   \subsection vide VIDE with Borland's Free Command Line Compiler
     -# Unzip the distribution into the folders it wants.
     -# Start VIDE and open the project file (paradigm_2.vpj)
        in the paradigm directory.
     -# Click on make
     -# Open a Windows command console window and switch to the paradigm
        directory by typing: <b>cd c:\\paradigm</b> or to the path
        you placed it.
     -# Run the server by typing: <b>paradigm</b><p>

   \subsection vc Microsoft Visual C++ 6.0 (Visual Studio)
     -# Unzip the distribution into the folders it wants.
     -# Start Visual Studio and open the project workspace (paradigm.dsw)
        in the paradigm directory.
     -# Click build
     -# Open a Windows command console window and switch to the paradigm
        directory by typing: <b>cd c:\\paradigm\\Debug</b> or to the path
        you placed it.  The default build is set to go to Debug folder.
     -# Run the server by typing: <b>paradigm</b><p>

 \section license License
  Paradigm was &copy; 2003 by Jon A. Lambert - All Rights Reserved.<br>
  Paradigm has been released to the public domain by Jon A. Lambert.<br>

 \section misc Miscellaneous
  \subsection notes Notes on operation
    - @shutdown will shut down the server
    - anything else echos to all connected<p>

 \subsection future Future
   - It might be nice to support a some telnet negotiation.
   - Illustrations of other network designs, asynchronous sockets
     and/or IOCP edge detection.
   - A port to BSD or Linux<p>

 \subsection contact Contact and Other Information

  \author Jon A. Lambert<br>
     aka Tyche<br>
     Email: jlsysinc@alltel.net
  \date 05/02/2003
  \version 0.30
*/

/*! \file paradigm.cpp
  This is the main entry point that starts the Paradigm server

  \author Jon A. Lambert
  \date 05/02/2003
  \version 0.30

  \notes
  Commands<br>
    - @shutdown - shuts down the server
 */
#include "sysconfig.h"
#include "server.h"
#include "event.h"
#include "eventqueue.h"
#include "user.h"

#ifdef __BORLANDC__
#if (__BORLANDC__ < 0x550)
#include <condefs.h>
USEUNIT("connection.cpp");
USEUNIT("server.cpp");
USEUNIT("socket.cpp");
USEUNIT("log.cpp");
USEUNIT("event.cpp");
USEUNIT("eventqueue.cpp");
USEUNIT("user.cpp");
//---------------------------------------------------------------------------
#endif
#endif

EventQueue* g_chat_to_server = 0;
EventQueue* g_server_to_chat = 0;
Log* g_log = 0;

#ifdef WIN32
void StartServer(void * parms);
#else
void* StartServer(void * parms);
#endif
void Chat();

//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char **argv)
{
  g_log = new Log("");

#ifdef WIN32
  WSADATA wsaData;  // This could be made global if we had a good reason.

  // Request Windows to start socket services.
  int err = WSAStartup(0x202,&wsaData);
  if (err) {
    cout << "ERROR-main(WSAStartup):" << err << endl;
    delete g_log;
    return -1;
  }
#endif

  g_chat_to_server = new EventQueue(0);
  g_server_to_chat = new EventQueue(0);
  if (!g_chat_to_server->IsOk() || !g_server_to_chat->IsOk()) {
    g_log->Write("ERROR-main(EventQueue): Unable to start event queues.");
    delete g_log;
    return -1;
  }
  Server* server = new Server(*g_chat_to_server, *g_server_to_chat, *g_log);
  if (!server->Boot(32000)) {
    g_log->Write("ERROR-main(Server): Unable to start server.");
    delete g_log;
    return -1;
  }
  g_log->Write("INFO-Main(): Server starting up");

  // Start up our Network server
  THREADHANDLE_T server_thread;
  StartThread(server_thread, StartServer, 8192, (void*)server);

  // Main thread will handle our Chat driver
  Chat();
  WaitForSingleObject(&server_thread, INFINITE);

#ifdef WIN32
  WSACleanup();  // Must always cleanup Windows sockets
#endif
  return 0;
}

/*!
 This is the thread that starts and runs the Server.
 */
#ifdef WIN32
void StartServer(void * parms) {
#else
void* StartServer(void * parms) {
#endif
  Server* server = (Server *)parms;
  if (server)
    server->Run(NULL);
  delete server;
#ifdef UNIX
  return NULL;
#endif
}

void Chat() {
  //! UserList is typedef'd here for ease of changing it later
  typedef list<User> UserList;

  UserList users;   //!< the list of Users managed by main()
  UserList::iterator u;
  Event* e;
  bool shutdown = false;

  while (!shutdown) {
    e = g_server_to_chat->Pop();
    if (!e) {
#ifdef WIN32
      Sleep(1);
#else
      usleep(1);
#endif
    } else {
      switch (e->mEventType) {
        case CONNECT_E: {
          char msg[] = "Welcome!\r\n";
          User user(e->mClientId);
          users.push_back(user);
          g_chat_to_server->Push(new Event(MESSAGE_E, user.Id(),
            strlen(msg), msg));
        }
        break;
        case DISCONNECT_E: {
          // A connection is lost - kill the its user in this subsystem.
          u = find(users.begin(),users.end(),e->mClientId);
          users.erase(u);
        }
        break;
        case MESSAGE_E: {
          // check for shutdown message
          // :WARN: test for null message as it should be just a CRLF
          if (e->mpData && !strncmp(e->mpData, "@shutdown", e->mDataLen)) {
            char msg[] = "Shutting down..BYE.\r\n";
            for(u = users.begin(); u != users.end(); u++) {
              g_chat_to_server->Push(new Event(MESSAGE_E, (*u).Id(),
                strlen(msg), msg));
            }
            g_chat_to_server->Push(new Event(SHUTDOWN_E, 0, 0, 0));
          } else { // otherwise we have a general message to echo
            string msg;
            if (e->mpData && e->mDataLen) {
              msg.append(e->mpData, e->mDataLen);
            }
            msg.append("\r\n");
            // message will be echoed to all clients
            for(u = users.begin(); u != users.end(); u++) {
              g_chat_to_server->Push(new Event(MESSAGE_E, (*u).Id(),
                msg.length(), msg.c_str()));
            }
          }
        }
        break;
        case SHUTDOWN_E:
          shutdown = true;
        break;
        case NONE_E:
        break;
      }  // switch
      delete e;
    }  // else
  } // while
}