TCP/IP Client/Server

The TCP Client/Server implements a minimum case server on both the local PC and on a Rabbit BL2100 Smartcat processor and a Client that talks to either of them.

Server.c

/***************************************************************************************
*  Copyright (c) 2007 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
*                                                                                      *
*  Server.c provides a PC based TCP/IP Socket Server that can be used to simulate the  *
*  RCM2200 embedded controller.                                                        *
*                                                                                      *
***************************************************************************************/

// See:  "http://msdn2.microsoft.com/en-us/library/ms740506.aspx"

#pragma warning ( disable : 4514 )     // unreferenced inline function has been removed

#pragma warning ( push, 3 )            // Suppress Warnings from WINDOWS.H and WINSOCK2.H
#include <WINSOCK2.H>                  // Windows Sockets Version 2.0
#pragma warning ( pop )                // Return to Normal Processing

#include <STDIO.H>                     // Definitions/Declarations for Standard I/O Routines

#pragma comment ( lib, "ws2_32.lib" )  // Functions Defined in WINSOCK2.H

#define DEFAULT_PORT 54321U            // The UDP Port Number                                               */

#define RANGE(SPECIFIED_VALUE,MIN_VALUE,MAX_VALUE) min ( max ( ( SPECIFIED_VALUE ), ( MIN_VALUE ) ), ( MAX_VALUE ) )

SOCKET            g_uListenSocket         = INVALID_SOCKET;       // The "Listen" Socket
SOCKET            g_uTCPSocket            = INVALID_SOCKET;       // The TCP/IP Connection Socket

static const char g_szDesc[]              = "UDP Server Rev 1.0"; // The UDP Server Description and Revision Number
BOOL              g_fMan                  = FALSE;                // TRUE = We are in Manual Mode, FALSE = We are in Automatic Mode
int               g_anVal [ 10 ]          = { 0 };                // Array of Integer Values

BOOL              g_fTheHostIsConnected   = FALSE;                // The TCP/IP Network Connection is Established

/**************************************************************************************
* Detect and Honor all of the Normal Shutdown Events, Including Ctrl+C and Ctrl+Break *
**************************************************************************************/

BOOL g_fShutDownRequested = FALSE;

static BOOL WINAPI ControlHandler ( DWORD dwCtrlType )

   {

   switch ( dwCtrlType )
      {
      case CTRL_C_EVENT:               /* Ctrl+C at the Keyboard           */
      case CTRL_BREAK_EVENT:           /* Ctrl+Break at the Keyboard       */
      case CTRL_CLOSE_EVENT:           /* Close at System Menu or Button   */
      case CTRL_LOGOFF_EVENT:          /* User Logoff Event                */
      case CTRL_SHUTDOWN_EVENT:        /* System Shutdown Event            */

         // Close the TCP/IP Sockets (Otherwise the System Hangs Waiting for Input)

         closesocket ( g_uTCPSocket    );  g_uTCPSocket     = INVALID_SOCKET;
         closesocket ( g_uListenSocket );  g_uListenSocket  = INVALID_SOCKET;

         // Request a Shutdown

         g_fShutDownRequested = TRUE;  /* Set Shutdown Requested           */

         return TRUE;

      default:  return FALSE;
      }

   }  /* static BOOL WINAPI ControlHandler ( DWORD dwCtrlType ) */

size_t ProcessCommands ( char * szRequest, char * szResponse, size_t cbResponse )

   {
   char  * pszCommand   = NULL;        /* Pointer to the Command String    */
   char  * pszValue     = NULL;        /* Pointer to the Value String      */
   char  * pszIndex     = NULL;        /* Pointer to the Index String      */

   int   nIndex         = 0;           /* Set to the Requested Array Index */

/*
 * Get the Command
 */
   if ( ( pszCommand = strtok ( szRequest, " " ) ) == NULL )
      pszCommand = szRequest;

/*
 * Process a "Des" Command
 */
   if ( strcmpi ( pszCommand, "Des" ) == 0 )
      {
      return _snprintf ( szResponse, cbResponse, g_szDesc );
      }

/*
 * Process a "Rev" Command
 */
   else if ( strcmpi ( pszCommand, "Rev" ) == 0 )
      {
      return _snprintf ( szResponse, cbResponse, strstr ( g_szDesc, "Rev" ) );
      }

/*
 * Process a "Man" Command - This is an Example of Setting a Boolean Value
 */
   else if ( strcmpi ( pszCommand, "Man" ) == 0 )
      {
      if ( ( pszValue = strtok ( NULL, " " ) ) != NULL )
         {
         g_fMan = RANGE ( atoi ( pszValue ), 0, 1 );
         }

      return _snprintf ( szResponse, cbResponse, "Man %d", g_fMan );
      }

/*
 * Process a "Val" Command - This is an Example of Setting an Element in an Array
 */
   else if ( strcmpi ( pszCommand, "Val" ) == 0 )
      {

   /*
    * "Val" with No Parameters Returns All Values
    */
      if ( ( pszIndex = strtok ( NULL, " " ) ) == NULL )
         {
         char  szValue  [  16 ]  = { 0 };       /* Area to Print the Value          */
         char  szBuffer [ 128 ]  = { 0 };       /* Area to Concatenate the Values   */

         strcpy ( szBuffer, "Val " );

         for ( nIndex = 0; nIndex < sizeof g_anVal / sizeof g_anVal [ 0 ]; nIndex++ )
            {
            sprintf ( szValue, "%d,", g_anVal [ nIndex ] ),
            strcat ( szBuffer, szValue );
            }

         szBuffer [ strlen ( szBuffer ) - 1 ] = '\0'; /* Remove the Trailing ',' */

         return _snprintf ( szResponse, cbResponse, "%s", szBuffer );
         }

   /*
    * "Val <index>" - Test for Legal Index
    */
      else
         {
         nIndex = atoi ( pszIndex );
         if ( nIndex < 0 || nIndex >= sizeof g_anVal / sizeof g_anVal [ 0 ] )
            {
            return _snprintf ( szResponse, cbResponse, "Val %d Illegal Index - Must Range Between 0 and %d", nIndex, sizeof g_anVal / sizeof g_anVal [ 0 ] - 1 );
            }
         }

   /*
    * "Val <index> <newvalue>"
    */
      if ( ( pszValue = strtok ( NULL, " " ) ) != NULL )
         {

      /*
       * Only Set to New Value if in Manual Mode
       */
         if ( ! g_fMan )
            {
            return _snprintf ( szResponse, cbResponse, "Must be in Manual Mode" );
            }

      /*
       * Set Value [ <index> ] to <NewValue> Within Range of 0 to 100
       */
         g_anVal [ nIndex ] = RANGE ( atoi ( pszValue ), 0, 100 );
         }

      return _snprintf ( szResponse, cbResponse, "Val %d %d", nIndex, g_anVal [ nIndex ] );
      }

/*
 * The Command is Invalid
 */
   else return _snprintf ( szResponse, cbResponse, "Valid Commands are:  Des, Rev, Man and Val" );

   }  /* size_t ProcessCommands ( char * szRequest, char * szResponse, size_t cbResponse ) */

int main ( int argc, char * argv [], char * envp [] )

   {
   WSADATA        stWSAData            = { 0U };               // The Windows Sockets Implementation Data

   SOCKET         uListenSocket        = INVALID_SOCKET;       // The "Listen" Socket
   SOCKET         uTCPSocket           = INVALID_SOCKET;       // The TCP/IP Connection Socket
   unsigned short usTCPPort            = DEFAULT_PORT;         // The Default TCP/IP Port Number

   SOCKADDR_IN    sinTCPServer         = { AF_INET };          // The TCP/IP Port
   SOCKADDR       sTCPClient           = { 0U };               // The Address of the External Connection
   int            nSockAddrLen         = sizeof sTCPClient;    // Used by the accept() Call to Pass the Address Length

   char           szRequest   [ 256 ]  = { 0 };                // Temporary Incoming Command Buffer
   char           szResponse  [ 256 ]  = { 0 };                // Temporary Incoming Command Buffer

   int            cbRecd               = 0;                    // The Number of Bytes Received
   int            cbSent               = 0;                    // The Number of Bytes Transmitted

   UNREFERENCED_PARAMETER ( argc );
   UNREFERENCED_PARAMETER ( argv );
   UNREFERENCED_PARAMETER ( envp );

   // Initiate the Windows Sockets Version 2.1

   if ( WSAStartup ( MAKEWORD ( 2, 1 ), &stWSAData ) != 0 )
      {
      int nErrorCode = WSAGetLastError(); /* i.e. WSASYSNOTREADY  */

      fprintf ( stderr, "WSAStartup() returned %d!\n\a", nErrorCode );
      return 1;
      }

   // Set the Port Number

   sinTCPServer.sin_port = htons ( usTCPPort );

   // Open the BL2600 Socket

   if ( ( g_uListenSocket = uListenSocket = socket ( PF_INET, SOCK_STREAM, IPPROTO_TCP ) ) == INVALID_SOCKET )
      {
      fprintf ( stderr, "socket() returned %d!\n\a", WSAGetLastError() );
      WSACleanup();
      return 2;
      }

   // Bind the Socket to the Port Address

   if ( bind ( uListenSocket, ( PSOCKADDR ) &sinTCPServer, sizeof sinTCPServer ) )
      {
      fprintf ( stderr, "bind() returned %d!\n\a", WSAGetLastError() );
      closesocket ( uListenSocket );
      WSACleanup();
      return 3;
      }

   // Put the Socket into Listen Mode

   if ( listen ( uListenSocket, 5 ) == SOCKET_ERROR ) // WSAEOPNOTSUPP
      {
      fprintf ( stderr, "listen() returned %d!\n\a", WSAGetLastError() );
      closesocket ( uListenSocket );
      WSACleanup();
      return 4;
      }

   // Bump to the Maximum Process and Thread Priority

   if ( ! SetPriorityClass ( GetCurrentProcess(), REALTIME_PRIORITY_CLASS ) )
      {
      fprintf ( stderr, "SetPriorityClass() returned %d!\n\a", GetLastError() );
      return 4;
      }

   if ( ! SetThreadPriority ( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL ) )
      {
      fprintf ( stderr, "SetThreadPriority() returned %d!\n\a", GetLastError() );
      return 5;
      }

   // Connection Loop

   fprintf ( stdout, "TCP/IP Port %u Online!\n\n", DEFAULT_PORT );

   SetConsoleCtrlHandler ( ControlHandler, TRUE );

   while ( ! g_fShutDownRequested )
      {

      // Wait for an Incoming Connection to be Established

      printf ( "Waiting for a Connection ...\n" );

      if ( ( g_uTCPSocket = uTCPSocket = accept ( uListenSocket, &sTCPClient, &nSockAddrLen ) ) == INVALID_SOCKET )
         {
         if ( g_fShutDownRequested ) break;

         fprintf ( stderr, "listen() returned %d!\n\a", WSAGetLastError() );
         closesocket ( uListenSocket );
         WSACleanup();

         return 5;
         }

      g_fTheHostIsConnected = TRUE;

      printf ( "\rConnection Established...\n" );

      // Request/Response Loop

      while  ( ! g_fShutDownRequested && uListenSocket != INVALID_SOCKET )
         {

         memset ( szRequest, 0, sizeof szRequest );

         if ( ( cbRecd = recv ( uTCPSocket, szRequest, sizeof szRequest - 1, 0 ) ) == 0 )
            break;
         if ( cbRecd == SOCKET_ERROR )
            {
            int nError = WSAGetLastError();

            if ( nError == WSAECONNABORTED || nError == WSAECONNRESET )
               break;

            fprintf ( stderr, "recv() returned %d!\n\a", nError );

            continue;
            }

         ProcessCommands ( szRequest, szResponse, sizeof szResponse );

         cbSent = send ( uTCPSocket, szResponse, strlen ( szResponse ), 0 );

         printf ( "%s\n", szResponse );

         }  // End - Request/Response Loop

      g_fTheHostIsConnected = FALSE;

      fprintf ( stdout, "Connection Closed ...\n" );

      }  // End - Connection Loop

   // Close the TCP/IP Sockets

   closesocket ( uTCPSocket );

   uTCPSocket = INVALID_SOCKET;

   // Cleanup and Terminate Windows Sockets Version 2.1

   WSACleanup();

   // Return Success!

   fprintf ( stdout, "\nBye!\n" );

   return 0;

   }  // int main ( int argc, char * argv [], char * envp [] )

Smartcat.c

/**************************************************************************************
* Copyright (c) 2010 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
**************************************************************************************/

/*********************************************************************************************
*                                                                                            *
* Smartcat.c implements a minimum case TCP/IP command server on Port 54321U and is addressed *
* by the Client.c implementation.                                                            *
*                                                                                            *
* Valid Commands are:  Des, Rev, Man and Val                                                 *
*                                                                                            *
* This Smartcat version closely replicates the Server.c version.                             *
*                                                                                            *
*********************************************************************************************/

#define USE_ETHERNET             1                          // New for DCRABBIT_9.21 Compiler

#define MY_IP_ADDRESS            "192.168.10.147"           // The Address of the Smartcat
#define MY_NETMASK               "255.255.255.0"            // The Address Netmask

#define DEFAULT_PORT             54321U                     // The TCP/IP Port Number

#define TCPCONFIG                0                          // Use the TCP/IP Address Information Defined Above

#class auto

#memmap xmem

#use "dcrtcp.lib"

#define RANGE(SPECIFIED_VALUE,MIN_VALUE,MAX_VALUE) min ( max ( ( SPECIFIED_VALUE ), ( MIN_VALUE ) ), ( MAX_VALUE ) )

typedef int BOOL;

static const char g_szDesc[]     = "TCP/IP Server Rev 1.0"; // The TCP/IP Server Description and Revision Number
BOOL              g_fMan;                                   // TRUE = We are in Manual Mode, FALSE = We are in Automatic Mode
int               g_anVal [ 10 ];                           // Array of Integer Values

size_t ProcessCommands ( char * szRequest, char * szResponse, size_t cbResponse )

   {
   char  * pszCommand;                                      // Pointer to the Command String
   char  * pszValue;                                        // Pointer to the Value String
   char  * pszIndex;                                        // Pointer to the Index String

   int   nIndex;                                            // Set to the Requested Array Index
   char  szValue  [  16 ];                                  // Area to Print the Value
   char  szBuffer [ 128 ];                                  // Area to Concatenate the Values

   // Initialize Variables

   pszCommand  = pszValue = pszIndex = NULL;

   nIndex      = 0;

   // Get the Command

   if ( ( pszCommand = strtok ( szRequest, " " ) ) == NULL )
      pszCommand = szRequest;

   // Process a "Des" Command

   if ( strcmpi ( pszCommand, "Des" ) == 0 )
      {
      return sprintf ( szResponse, g_szDesc );
      }

   // Process a "Rev" Command

   else if ( strcmpi ( pszCommand, "Rev" ) == 0 )
      {
      return sprintf ( szResponse, strstr ( g_szDesc, "Rev" ) );
      }

   // Process a "Man" Command - This is an Example of Setting a Boolean Value

   else if ( strcmpi ( pszCommand, "Man" ) == 0 )
      {
      if ( ( pszValue = strtok ( NULL, " " ) ) != NULL )
         {
         g_fMan = RANGE ( atoi ( pszValue ), 0, 1 );
         }

      return sprintf ( szResponse, "Man %d", g_fMan );
      }

   // Process a "Val" Command - This is an Example of Setting an Element in an Array

   else if ( strcmpi ( pszCommand, "Val" ) == 0 )
      {

      // "Val" with No Parameters Returns All Values

      if ( ( pszIndex = strtok ( NULL, " " ) ) == NULL )
         {
         memset ( szValue, 0, sizeof szValue );
         memset ( szBuffer, 0, sizeof szBuffer );

         strcpy ( szBuffer, "Val " );

         for ( nIndex = 0; nIndex < sizeof g_anVal / sizeof g_anVal [ 0 ]; nIndex++ )
            {
            sprintf ( szValue, "%d,", g_anVal [ nIndex ] ),
            strcat ( szBuffer, szValue );
            }

         // Remove the Trailing ',' left by Concatenation

         szBuffer [ strlen ( szBuffer ) - 1 ] = '\0';

         return sprintf ( szResponse, "%s", szBuffer );
         }

      // "Val <index>" - Test for Legal Index

      else
         {
         nIndex = atoi ( pszIndex );
         if ( nIndex < 0 || nIndex >= sizeof g_anVal / sizeof g_anVal [ 0 ] )
            {
            return sprintf ( szResponse, "Val %d Illegal Index - Must Range Between 0 and %d", nIndex, sizeof g_anVal / sizeof g_anVal [ 0 ] - 1 );
            }
         }

      // "Val <index> <newvalue>"

      if ( ( pszValue = strtok ( NULL, " " ) ) != NULL )
         {

         // Only Set to New Value if in Manual Mode

         if ( ! g_fMan )
            {
            return sprintf ( szResponse, "Must be in Manual Mode" );
            }

         // Set Value [ <index> ] to <NewValue> Within Range of 0 to 100

         g_anVal [ nIndex ] = RANGE ( atoi ( pszValue ), 0, 100 );
         }

      return sprintf ( szResponse, "Val %d %d", nIndex, g_anVal [ nIndex ] );
      }

   // The Command is Invalid

   else return sprintf ( szResponse, "Valid Commands are:  Des, Rev, Man and Val" );

   }  // size_t ProcessCommands ( char * szRequest, char * szResponse, size_t cbResponse )

void main ( void )

   {
   tcp_Socket  sTCPSocket;

   int         nResult;

   int         cbRecd;
   int         cbSent;

   byte        szRequest   [ 128 ];
   byte        szResponse  [ 128 ];

   // Print this Smartcat's MAC Address

   printf (

      "The MAC Address of this Smartcat is %02X %02X %02X %02X %02X %02X\n",

      SysIDBlock.macAddr [ 0 ], SysIDBlock.macAddr [ 1 ], SysIDBlock.macAddr [ 2 ],
      SysIDBlock.macAddr [ 3 ], SysIDBlock.macAddr [ 4 ], SysIDBlock.macAddr [ 5 ]

   );

   // Initialize Sockets

   if ( ( nResult = sock_init() ) != 0 )
      {
      printf ( "Error:  sock_init() returned %d\n\a\a\a", nResult );
      return;
      }

   // Print the TCP/IP Address Info

   ip_print_ifs();

   // Print the Port Number

   printf ( "\nTCP/IP Port:  %u\n\n", DEFAULT_PORT );

   // Initialize the Global Values

   g_fMan = FALSE;

   memset ( g_anVal, 0, sizeof g_anVal );

   // Initialization Required for BL2100 Series Boards

   brdInit();

   for (;;)
      {

      tcp_tick ( NULL );

      // Establish a Connection

      tcp_listen ( &sTCPSocket, DEFAULT_PORT, 0, 0, NULL, 0 );

      // Wait for an Incoming Connection

      printf ( "Waiting for connection...\n" );

      while ( ! sock_established ( &sTCPSocket ) && sock_bytesready ( &sTCPSocket ) == -1 ) tcp_tick ( NULL );

      printf ( "Connection received...\n" );

      // Process the Request/Response Loop

      for (;;)
         {

         // Allow Time for TCP/IP Processing to Happen

         tcp_tick ( NULL );

         // Zero Out the Request/Response Buffers

         memset ( szRequest,  0, sizeof ( szRequest   ) );
         memset ( szResponse, 0, sizeof ( szResponse  ) );

         // Read and Process a Command

         if ( ( cbRecd = sock_fastread ( &sTCPSocket, szRequest, sizeof szRequest ) ) < 0 ) break;

         // Ignore "Zero Bytes Returned" if No Incoming Request Yet

         if ( ! cbRecd ) continue;

         // Print the Incoming Request

         printf ( "[%d]==>  %s\n", cbRecd, szRequest );

         // Process the Command

         ProcessCommands ( szRequest, szResponse, sizeof szResponse );

         // Return the Results

         printf ( "%s\t\t\n", szResponse );

         cbSent = sock_write ( &sTCPSocket, ( byte * ) szResponse, sizeof szResponse );

         }  // End - Process the Request/Response Loop

      // Notify the User that We Lost Our Connection

      printf ( "Connection closed...\n" );

      }  // End - Establish a Connection

   }  // void main ( void )


Client.c

/*********************************************************************************************
* Copyright (c) 2007 - 2010 David Cass Tyler, PO Box 1026, Willard, NM 87063, (505) 384-5342 *
*********************************************************************************************/

/***************************************************************************************
*                                                                                      *
* Client.c implements client for the minimum case UDP command server on Port 50001.    *
* by the Client.c implementation.                                                      *
*                                                                                      *
* Run as:  "Client [ <server> [ <port> ] ]"                                            *
*                                                                                      *
* You can process commands from an external file:  "Client < Commands"                 *
*                                                                                      *
* To compile from the command line:  "CL Client.c"                                     *
*                                                                                      *
*  Derived from the Circuit Cellar Issue #211 (February 2008) article RCM2200 code.    *
*                                                                                      *
***************************************************************************************/

#pragma warning ( disable : 4514 )     /* unreferenced inline function has been removed         */

#pragma warning ( push, 3 )            /* Suppress Warnings from WINDOWS.H and WINSOCK2.H       */
#include <WINSOCK2.H>                  /* Windows Sockets Version 2.0                           */
#pragma warning ( pop )                /* Return to Normal Processing                           */

#include <IO.H>                        /* _isatty ( _fileno ( stdin ) ) );                      */
#include <STDIO.H>                     /* Definitions/Declarations for Standard I/O Routines    */

#pragma comment ( lib, "ws2_32.lib" )  /* Functions Defined in WINSOCK2.H                       */

/**************************************************************************************
* Detect and Honor all of the Normal Shutdown Events, Including Ctrl+C and Ctrl+Break *
**************************************************************************************/

BOOL fShutDownRequested = FALSE;

static BOOL WINAPI ControlHandler ( DWORD dwCtrlType )

   {

   switch ( dwCtrlType )
      {
      case CTRL_C_EVENT:               /* Ctrl+C from Keyboard                                  */
      case CTRL_BREAK_EVENT:           /* Ctrl+Break from Keyboard                              */
      case CTRL_CLOSE_EVENT:           /* Close from System Menu or Button                      */
      case CTRL_SHUTDOWN_EVENT:        /* System Shutdown Event                                 */
      case CTRL_LOGOFF_EVENT:          /* User Logoff Event - DON'T Shut Down for User Logoff   */

         fclose ( stdin );             /* Close the Input so the System doesn't Hang            */

         fShutDownRequested = TRUE;    /* Set Shutdown Requested                                */
         return TRUE;

      default:
         fprintf ( stdout, "ControlHandler() Received %lu\n\a", dwCtrlType );
         return FALSE;
      }

   }  /* static BOOL WINAPI ControlHandler ( DWORD dwCtrlType ) */

BOOL GetTCPIPAddress ( LPCSTR strComputerAddress, char * szAddress, size_t cbAddress )

   {
   struct hostent       * lpHostEntry  = gethostbyname ( strComputerAddress );
   struct sockaddr_in   stDestAddr     = { 0 };
   char                 * pszHostName  = NULL;

   if ( lpHostEntry )
      {
      memcpy ( &stDestAddr.sin_addr, lpHostEntry->h_addr_list [ 0 ], min ( sizeof stDestAddr.sin_addr, lpHostEntry->h_length ) );
      stDestAddr.sin_family = lpHostEntry->h_addrtype;
      }
   else
      {
      unsigned long lnAddr = 0LU;

      if ( ( lnAddr = inet_addr ( strComputerAddress ) ) == INADDR_NONE )
         {
         strncpy ( szAddress, "???.???.???.???", cbAddress );
         return FALSE;
         }
      }

   // Convert the Destination Address Into a Printable String

   pszHostName = inet_ntoa ( stDestAddr.sin_addr );

   strncpy ( szAddress, pszHostName, cbAddress );

   return TRUE;

   }  // BOOL GetTCPIPAddress ( LPCSTR strComputerAddress, char * szAddress, size_t cbAddress )

int main ( int argc, char * argv[], char * envp [] )

   {
   SOCKET         uTCPSocket           = INVALID_SOCKET;                   /* The TCP/IP Communications Socket          */

   SOCKADDR_IN    sinTCP            = { AF_INET, 0U };                     /* The Socket Intput Address                 */

   int            nTimeout             = 100;                              /* Desired Timeout Value in Milliseconds     */
   int            nResult              = 0;                                /* Error Return                              */

   WSADATA        stWSAData            = { 0U };                           /* The Windows Sockets Implementation Data   */

   char           * szHost             = NULL;                             /* The Host Name Parameter                   */
   char           szTCPAddress [ 16 ]  = "";                               /* The TCP/IP Address of the Specified Host  */

   BOOL           fRedirected          = ! _isatty ( _fileno ( stdin ) );  /* Are Commands Comming from a Piped File?   */

   int            cbSent               = 0;                                /* The Number of Bytes Sent                  */
   int            cbRecd               = 0;                                /* The Number of Bytes Received              */

   char           * szDefaultHost      = "localhost";                      /* The Default TCP/IP Address                */
   unsigned short usPortNumber         = 54321U ;                          /* The TCP/IP Socket Port Number             */

/*
 * Account for Unreferenced Parameters
 */
   UNREFERENCED_PARAMETER ( envp );

/*
 * Start Up a Windows Socket Application
 */
   if ( WSAStartup ( MAKEWORD ( 2, 1 ), &stWSAData ) != 0 )
      {
      int nErrorCode = WSAGetLastError();
      fprintf ( stderr, "WSAStartup() returned %d!\n\a", nErrorCode );
      return 1;
      }

/*
 * Determine the TCP/IP Address and Socket Number to Use
 */
   szHost = argc > 1 ? argv [ 1 ] : szDefaultHost;

   if ( ! GetTCPIPAddress ( szHost, szTCPAddress, sizeof szTCPAddress ) )
      {
      fprintf ( stderr, "Host %s not found!\n\a", szHost );
      WSACleanup();
      return 2;
      }

   if ( argc > 2 )
      usPortNumber = ( unsigned short ) atoi ( argv [ 2 ] );

   sinTCP.sin_port = htons ( usPortNumber );

/*
 * Open the TCP/IP Socket
 */
   if ( ( uTCPSocket = socket ( AF_INET, SOCK_STREAM, 0 ) ) == INVALID_SOCKET )
      {
      int nErrorCode = WSAGetLastError();
      fprintf ( stderr, "socket() returned %d!\n\a", nErrorCode );
      WSACleanup();
      return 3;
      }

/*
 * Set the Send and Receive Timeouts
 */
   nResult = setsockopt ( uTCPSocket, SOL_SOCKET, SO_SNDTIMEO, ( char * ) &nTimeout, sizeof ( nTimeout ) );
   nResult = setsockopt ( uTCPSocket, SOL_SOCKET, SO_RCVTIMEO, ( char * ) &nTimeout, sizeof ( nTimeout ) );

/*
 * Get the Socket Address
 */
   sinTCP.sin_addr.S_un.S_addr = inet_addr ( szTCPAddress );

/*
 * Connect it to the Remote Socket
 */
   if ( connect ( uTCPSocket, ( PSOCKADDR ) &sinTCP, sizeof ( sinTCP ) ) == SOCKET_ERROR )
      {
      int nErrorCode = WSAGetLastError(); /* i.e. WSAECONNREFUSED or WSAEADDRNOTAVAIL */
      closesocket ( uTCPSocket );
      uTCPSocket = INVALID_SOCKET;
      WSACleanup();
      fprintf ( stderr, "connect() returned %d <%s>!\n\a", nErrorCode, nErrorCode == WSAECONNREFUSED ? "Connection Refused" : "<unknown>" );
      return 4;
      }

/*
 * Do Normal Processing Here
 */
   SetConsoleCtrlHandler ( ControlHandler, TRUE );

/*************************************
* Process the Request/Response Pairs *
*************************************/

   while ( ! fShutDownRequested )
      {
      char  szRequest   [ 1024 ] = { 0 };
      char  szResponse  [ 1024 ] = { 0 };

      int   i                    = 0;

   /*
    * Get and Trim the Command String
    */
      fprintf ( stdout, "==>  " );

      if ( ! fgets ( szRequest, sizeof szRequest, stdin ) )
         break;

   /*
    * Find the Terminating '\0'
    */
      for ( i = 0; szRequest [ i ]; i++ );

   /*
    * Delete Trailing Newlines and Carriage Returns
    */
      while ( i && ( szRequest [ i - 1 ] == '\r' || szRequest [ i - 1 ] == '\n' ) )
         szRequest [ --i ] = '\0';

      if ( fRedirected )
         fprintf ( stdout, "%s\n", szRequest );

   /*
    * Send the Request
    */
      if ( ( cbSent = send ( uTCPSocket, strupr ( szRequest ), strlen ( szRequest ), MSG_DONTROUTE ) ) == SOCKET_ERROR )
         {
         fprintf ( stderr, "send():  send() returned %d!\n", WSAGetLastError() );
         continue;
         }

   /*
    * Get the Response
    */
      if ( ( cbRecd = recv ( uTCPSocket, szResponse, sizeof szResponse, 0 ) ) == SOCKET_ERROR )
         {
         fprintf ( stderr, "recv():  recv() returned %d!\n", WSAGetLastError() );
         continue;
         }

   /*
    * Print the Results
    */
      fprintf ( stdout, "%s\n", szResponse );

      }  /* End - while ( ! fShutDownRequested ) */

/*
 * Close the Socket
 */
   closesocket ( uTCPSocket );
   uTCPSocket = INVALID_SOCKET;

/*
 * Cleanup and Terminate Windows Sockets
 */
   WSACleanup();

/*
 * Return Success!
 */
   fprintf ( stdout, "\n\nBye!\n" );

   return 0;

   }  /* int main ( int argc, char * argv [], char * envp [] ) */