Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

A Portable Multithreaded Web Server


DDJ, Spring97: Listings

A Portable Multithreaded Web Server

Spring 1997, Dr. Dobb's Journal

by Pieter Hintjens and Pascal Antonnaux


Listing One

#include "sfl.h"  /*  SFL library header file */
#include "smtlib.h"  /*  SMT kernel functions */

int
main (int argc, char *argv [])
{
    if (argc > 1) /*  Use port base if specified */
    ip_portbase = atoi (argv [1]);

    smt_init ();  /*  Initialise SMT kernel*/
    smthttp_init (); /*  Initialise HTTP server  */
    smtecho_init (); /* and ECHO server */
    smt_exec_full ();/*  Run until completed  */
    smt_term ();  /*  Shut-down SMT kernel */
    return (EXIT_SUCCESS);
}


Listing Two

/*  ----------------------------------------------------------------<Prolog>-
    Name:       smtecho.c
    Title:      SMT TCP/IP echo agent
    Package:    Libero/SMT Kernel 2.x

    Written:    96/06/15  Pieter Hintjens <[email protected]>
    Revised:    96/09/07  Pieter Hintjens <[email protected]>

    Synopsis:   Handles the TCP/IP echo protocol.  Initialises automatically
                when you call the smtecho_init() function.  Uses smtsock.
                Sends errors to the SMTOPER agent.   Logs connections to the
                file smtecho.log via SMTLOG agent.

    Copyright:  Copyright (c) 1991-1996 iMatix
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version. This program is distributed in the hope that it will be
    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
    Public License for more details.  You should have received a copy of the
    GNU General Public License along with this program; if not, write to the
    Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ------------------------------------------------------------------</Prolog>-*/

#include "sfl.h"                        /*  SFL library header file          */
#include "smtlib.h"                     /*  SMT kernel functions             */


/*- Definitions -------------------------------------------------------------*/

#define AGENT_NAME   SMT_ECHO           /*  Our public name                  */

typedef struct {                        /*  Thread context block:            */
    event_t thread_type;                /*    Thread type indicator          */
    SOCKET  handle;                     /*    Handle for i/o                 */
} TCB;


/*- Global variables used in this source file only --------------------------*/

static TCB
    *tcb;                               /*  Address thread contect block     */
static QID
    console,                            /*  Operator console event queue     */
    logq,                               /*  Logging agent event queue        */
    sockq;                              /*  Socket agent event queue         */
static char
    msg_body [LINE_MAX];                /*  Message sent to socket agent     */
static int
    msg_size;                           /*  Size of formatted msg_body       */
static DESCR                            /*  Descriptor for exdr_writed       */
    msg = { LINE_MAX, (byte *) msg_body };

#include "smtecho.d"                    /*  Include dialog data              */

/********************   INITIALIZE AGENT - ENTRY POINT   *********************/

int smtecho_init (void)
{
    AGENT   *agent;                     /*  Handle for our agent             */
    THREAD  *thread;                    /*  Handle to various threads        */
#   include "smtecho.i"                 /*  Include dialog interpreter       */

    /*                      Method name      Event value     Priority        */
    /*  Shutdown event comes from Kernel                                     */
    method_declare (agent, "SHUTDOWN",       shutdown_event, SMT_PRIORITY_MAX);

    /*  Reply events from socket agent                                       */
    method_declare (agent, "SOCK_INPUT_OK",  ok_event,       0);
    method_declare (agent, "SOCK_OUTPUT_OK", ok_event,       0);
    method_declare (agent, "SOCK_READ_OK",   read_ok_event,  0);
    method_declare (agent, "SOCK_WRITE_OK",  write_ok_event, 0);
    method_declare (agent, "SOCK_CLOSED",    closed_event,   0);
    method_declare (agent, "SOCK_ERROR",     error_event,    0);
    method_declare (agent, "SOCK_TIMEOUT",   error_event,    0);

    /*  Private methods used to pass initial thread events                   */
    method_declare (agent, "_MASTER",        master_event,   0);
    method_declare (agent, "_CLIENT",        client_event,   0);

    /*  Ensure that operator console is running, else start it up            */
    smtoper_init ();
    if ((thread = thread_lookup (SMT_OPERATOR, "")) != NULL)
        console = thread-> queue-> qid;
    else
        return (-1);
    /*  Ensure that socket agent is running, else start it up                */
    smtsock_init ();
    if ((thread = thread_lookup (SMT_SOCKET, "")) != NULL)
        sockq = thread-> queue-> qid;
    else
        return (-1);
    /*  Ensure that logging agent is running, and create new thread          */
    smtlog_init ();
    if ((thread = thread_create (SMT_LOGGING, "")) != NULL)
        logq = thread-> queue-> qid;        /*  Get logging queue id         */
    else
        return (-1);
    /*  Create initial thread to manage master port                          */
    if ((thread = thread_create (AGENT_NAME, "")) != NULL)
      {
        SEND (&thread-> queue-> qid, "_MASTER", "");
        ((TCB *) thread-> tcb)-> thread_type = master_event;
        ((TCB *) thread-> tcb)-> handle = 0;
      }
    else
        return (-1);
    /*  Signal okay to caller that we initialised okay                       */
    return (0);
}

/*************************   INITIALIZE THE THREAD   *************************/
MODULE initialise_the_thread (THREAD *thread)
{
    /*  We don't set the_next_event because we expect an argument event      */
    /*  to supply these.                                                     */

    tcb = thread-> tcb;                 /*  Point to thread's context        */
}

/**************************   OPEN AGENT LOG FILE    *************************/
MODULE open_agent_log_file (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    sendfmt (&logq, "OPEN", "smtecho.log");
}

/***************************   OPEN MASTER SOCKET   **************************/
MODULE open_master_socket (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */

    tcb-> handle = passive_TCP (SMT_ECHO_PORT, 5);
    if (tcb-> handle == INVALID_SOCKET)
      {
        sendfmt (&console, "ERROR",
                 "Could not open ECHO port %d/%s", ip_portbase, SMT_ECHO_PORT);
        sendfmt (&console, "ERROR",
                 connect_errlist [connect_error ()]);
        raise_exception (fatal_event);
      }
    else
        sendfmt (&logq, "PUT", "I: opened echo port %s", SMT_ECHO_PORT);
}

/*************************   WAIT FOR SOCKET INPUT   *************************/
MODULE wait_for_socket_input (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */

    msg_size = exdr_writed (&msg, SMT_SOCK_INPUT, 0,
                            tcb-> handle, (qbyte) 0);
    event_send (
        &sockq,                         /*  Send to socket agent             */
        &thread-> queue-> qid,          /*  Queue for reply                  */
        "INPUT",                        /*  Name of event to send            */
        msg_body, msg_size,             /*  Event body and size              */
        NULL, NULL, NULL,               /*  No response events               */
        0);                             /*  No timeout                       */
}

/************************   ACCEPT CLIENT CONNECTION   ***********************/
MODULE accept_client_connection (THREAD *thread)
{
    SOCKET
        slave_socket;                   /*  Connected socket                 */
    THREAD
        *child_thread;                  /*  Handle to child threads          */

    tcb = thread-> tcb;                 /*  Point to thread's context        */

    slave_socket = accept_socket (tcb-> handle);
    if (slave_socket != INVALID_SOCKET)
      {
        child_thread = thread_create (AGENT_NAME, "");
        if (child_thread)
          {
            SEND (&child_thread-> queue-> qid, "_CLIENT", "");
            ((TCB *) child_thread-> tcb)-> handle      = slave_socket;
            ((TCB *) child_thread-> tcb)-> thread_type = client_event;
          }
      }
    else
    if (errno != EAGAIN)
      {
        sendfmt (&console, "ERROR",
                 "Could not accept connection: %s", sockmsg ());
        raise_exception (exception_event);
      }
}

/**********************   READ SOCKET DATA REPEATEDLY   **********************/
MODULE read_socket_data_repeatedly (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */

    /*  We ask the socket agent to read what it can from the socket,         */
    /*  and to return that to us as a message; from 1 to 2048 bytes.         */
    /*  Each time input arrives, we'll get an event.                         */
    msg_size = exdr_writed (&msg, SMT_SOCK_READ, 0,
                            tcb-> handle, 2048, 1, (qbyte) 0);
    event_send (
        &sockq,                         /*  Send to socket agent             */
        &thread-> queue-> qid,          /*  Queue for reply                  */
        "READR",                        /*  Name of event to send            */
        msg_body, msg_size,             /*  Event body and size              */
        NULL, NULL, NULL,               /*  No response events               */
        0);                             /*  No timeout                       */
}

/***************************   WRITE SOCKET DATA   ***************************/
MODULE write_socket_data (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */

    /*  We just need to bounce the read data right back, since it is in      */
    /*  the correct format already (SMT_SOCK_WRITE == SMT_SOCK_READ_OK)      */
    ASSERT (streq (SMT_SOCK_WRITE, SMT_SOCK_READ_OK));
    event_send (
        &sockq,                         /*  Send to socket agent             */
        &thread-> queue-> qid,          /*  Queue for reply                  */
        "WRITE",                        /*  Name of event to send            */
        thread-> event-> body,          /*  Event body                       */
        thread-> event-> body_size,     /*    and size                       */
        NULL, NULL, NULL,               /*  No response events               */
        0);                             /*  No timeout                       */
    event_wait ();                      /*  Wait for reply event             */
}

/**************************   SIGNAL SOCKET ERROR   **************************/
MODULE signal_socket_error (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    sendfmt (&console, "ERROR", "Error on socket: %s", thread-> event-> body);
}

/***************************   CHECK SOCKET TYPE   ***************************/
MODULE check_socket_type (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    the_next_event = tcb-> thread_type;
}

/*************************   CLOSE AGENT LOG FILE    *************************/
MODULE close_agent_log_file (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    sendfmt (&logq, "CLOSE", "");
}

/************************   SHUTDOWN THE APPLICATION   ***********************/
MODULE shutdown_the_application (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    smt_shutdown ();                    /*  Halt the application             */
}

/*************************   TERMINATE THE THREAD   **************************/
MODULE terminate_the_thread (THREAD *thread)
{
    tcb = thread-> tcb;                 /*  Point to thread's context        */
    if (tcb-> handle);
        close_socket (tcb-> handle);
    the_next_event = terminate_event;
}


End Listings


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.