home *** CD-ROM | disk | FTP | other *** search
/ Network Support Encyclopedia 96-1 / novell-nsepro-1996-1-cd2.iso / download / netware / spxex1.exe / SPXCHAT.CPP < prev    next >
C/C++ Source or Header  |  1995-03-31  |  20KB  |  681 lines

  1.  
  2. /****************************************************************************
  3. ** File: SPXCHAT.CPP
  4. **
  5. **    Description:
  6. **
  7. **          Sample OS/2 application that demonstrates sending and receiving
  8. **          SPX packets in OS/2.  Note the inclusion of dos16.h, which
  9. **          is where the 16 bit semaphore calls are prototyped.
  10. **
  11. **          This program uses the following command-line syntax.
  12. **          Usage: SPXCHAT  [SERVER]|[CLIENT <Address>]
  13. **          Address = Net/Node (in hex, leading zero's required)
  14. **          Example:  SPXCHAT SERVER   -or-   SPXCHAT CLIENT 00001234/00001b02362
  15. **
  16. **
  17. **
  18. **    Disclaimer:
  19. **
  20. **         Novell, Inc. makes no representations or warranties with respect to
  21. **         any NetWare software, and specifically disclaims any express or
  22. **         implied warranties of merchantability, title, or fitness for a
  23. **         particular purpose.
  24. **
  25. **         Distribution of any NetWare software is forbidden without the
  26. **         express written consent of Novell, Inc.  Further, Novell reserves
  27. **         the right to discontinue distribution of any NetWare software.
  28. **
  29. **         Novell is not responsible for lost profits or revenue, loss of use
  30. **         of the software, loss of data, costs of re-creating lost data, the
  31. **         cost of any substitute equipment or program, or claims by any party
  32. **         other than you.  Novell strongly recommends a backup be made before
  33. **         any software is installed.   Technical support for this software
  34. **         may be provided at the discretion of Novell.
  35. **
  36. **    Programmers:
  37. **
  38. **         Ini    Who                    Firm
  39. **         -------------------------------------------------------------------
  40. **         VLS    VL Smith
  41. **         DRS    Dan Stratton            Novell Developer Support
  42. **
  43. **    History:
  44. **
  45. **         When        Who    What
  46. **         -------------------------------------------------------------------
  47. **           11-12-93        VLS    First Code
  48. **         07-01-94        DRS    Modified for 1.0e SDK and 32 bit compilers
  49. **           03-30-95        DRS    Modified into SPX Chat for OS/2
  50. */
  51.  
  52.  
  53. #define INCL_BASE
  54. #define INCL_DOS
  55. #define INCL_VIO
  56. #define INCL_DOSPROCESS
  57.  
  58. #include <os2.h>
  59. #include <stdio.h>
  60. #include <string.h>
  61. #include <conio.h>
  62. #include <process.h>
  63. #include <errno.h>
  64. #include <ctype.h>
  65.  
  66.  
  67. #define SEMCALLSET         Dos16SemSet
  68. #define SEMCALLWAIT         Dos16SemWait
  69. #define MAX_MESSAGE_SIZE     254
  70. #define ERROR_COMPLETION     0xA000
  71. #define MY_SOCKET         0x4445
  72.  
  73.  
  74. #define IS32BIT
  75. #define NWOS2
  76. /* for now, just allow to run with either Borland or IBM */
  77. #if defined    (__BORLANDC__)
  78.     #define BCPP
  79.     void         Send(void *sendInfo);
  80.     void         Rec(void *recInfo);
  81. #elif (defined (__IBMC__) || defined(__IBMCPP__))
  82.     #define CSET2
  83.     #pragma    stack16(2048)
  84.     void  _Export _Optlink Send(void *sendInfo);
  85.     void  _Export _Optlink Rec(void *recInfo);
  86.  
  87. #endif
  88.  
  89. extern "C"{
  90. #include <nwcalls.h>
  91. #include <spxcalls.h>
  92. #include <spxerror.h>
  93. #include "dos16.h"
  94. }
  95.  
  96. typedef struct IPXAddress
  97.    {
  98.       BYTE    network[4];                    /* high-low */
  99.       BYTE    node[6];                       /* high-low */
  100.       BYTE    socket[2];                     /* high-low */
  101.    }IPXAddress;
  102.  
  103. typedef struct SPXInfoStruct
  104.   {
  105.     USHORT    connectionNumber;
  106.     BYTE    spxBuffer[MAX_MESSAGE_SIZE];
  107.     void NWPTR     semaph;
  108.     SPX_HEADER    spxHeader;
  109.     SPX_ECB    spxEcb;
  110.    }SPXInfoStruct;
  111.  
  112.  
  113. int        CheckForCompletion( SPX_ECB * ) ;
  114. void        GetWorkStationAddress(char *, char *) ;
  115. void        killConn(SPX_ECB *spxEcb, void NWPTR sem, USHORT connectionNumber, USHORT spxSocket);
  116. int           ParseAddress( char *address, IPXAddress *destination );
  117. int           PutAddress( char *string, char *buf, int hexBytes );
  118. int         clientProc(IPXAddress *tempAddress, USHORT *connectionNumber, void NWPTR *semaphore, SPX_ECB *spxEcb, SPX_HEADER *spxHeader,USHORT *spxSocket);
  119. int         serverProc(USHORT *connectionNumber,void NWPTR *semaphore, SPX_ECB *spxEcb, SPX_HEADER *spxHeader,USHORT *spxSocket);
  120.  
  121.  
  122. BOOL        terminateConnection = FALSE;             /* set in most error conditions */
  123. BOOL        wasKilled = FALSE;                       /* Tells us if the other guy killed the connection */
  124.  
  125.  
  126. /*  --------------------------------------------------------------------
  127.     ParseAddress and PutAddress build an internetwork address from an
  128.     input address from the user.  This address is then inserted into a
  129.     IPX_ADDR structure for use later in the program.
  130.     -------------------------------------------------------------------- */
  131.  
  132. int ParseAddress( char *addr, IPXAddress *destination )
  133. {
  134.     if ( strlen( addr ) == 21 && addr[ 8 ] == '/' )
  135.         if ( PutAddress( (char *)addr, (char *)destination->network, 4 ) )
  136.              if ( PutAddress( (char *)&addr[ 9 ],(char *)destination->node, 6 ) )
  137.             return 1;
  138.     return 0;
  139. } /* ParseAddress */
  140.  
  141. int PutAddress( char *string, char *buf, int hexBytes )
  142. {
  143.     int   i, j, value;
  144.     char   c;
  145.  
  146.     for ( i = 0; i < hexBytes; i++ )
  147.     {
  148.         value = 0;                  /* build a byte from two nibbles */
  149.         for ( j = 0; j < 2; j++ )
  150.         {
  151.         value <<= 4;
  152.         if ( ( c = toupper( *string ) ) >= '0' && c <= '9')
  153.            value += c - '0';
  154.            else if ( c >= 'A' && c <= 'F' )
  155.               value += c - 'A' + 10;
  156.            else
  157.               return 0;
  158.            string++;
  159.        }
  160.        *buf++ = value;
  161.     }
  162.     return 1;
  163. } /* PutAddress */
  164.  
  165.  
  166.  
  167. /* ---------------------------------------------------------------
  168.     Usage prints the correct command-line usage of this
  169.     sample application.
  170.    --------------------------------------------------------------- */
  171.  
  172. void usage(void)
  173. {
  174.  
  175.   printf( "Usage:\tSPXCHAT  [SERVER]|[CLIENT <Address>]\n" );
  176.   printf( "\tAddress = Net/Node (in hex, leading zero's required)\n" );
  177.   printf( "\tExample:\"SPXCHAT SERVER\n" );
  178.   printf( "\tOR \"SPXCHAT CLIENT 00001234/00001b02362e\"\n" );
  179.  
  180. }
  181.  
  182.  
  183. /* -----------------------------------------------------------
  184.     Generic send data function for both Server and
  185.     Client sides.  It prompts the user for data and
  186.     and sends it.  Packet length is limited to 254
  187.     bytes of data right now, simply because gets,
  188.     cgets, etc. only accepts that much data at a
  189.     time.
  190.    ----------------------------------------------------------- */
  191.  
  192. void Send(void *sendInfo)
  193. {
  194.    WORD        ccode = 0;
  195.   #if defined(BCPP)
  196.    char     tempspxBuffer[MAX_MESSAGE_SIZE+2];
  197.   #endif
  198.  
  199.  
  200.     ((SPXInfoStruct *)sendInfo)->semaph = 0L ;
  201.     ((SPXInfoStruct *)sendInfo)->spxEcb.hsem = &(((SPXInfoStruct *)sendInfo)->semaph);
  202.     ((SPXInfoStruct *)sendInfo)->spxEcb.fragCount = 2;
  203.     ((SPXInfoStruct *)sendInfo)->spxEcb.fragList[0].fragAddress = &(((SPXInfoStruct *)sendInfo)->spxHeader);
  204.     ((SPXInfoStruct *)sendInfo)->spxEcb.fragList[0].fragSize = sizeof(SPX_HEADER);
  205.     ((SPXInfoStruct *)sendInfo)->spxEcb.fragList[1].fragAddress = ((SPXInfoStruct *)sendInfo)->spxBuffer ;
  206.     ((SPXInfoStruct *)sendInfo)->spxEcb.fragList[1].fragSize = MAX_MESSAGE_SIZE;
  207.  
  208.    do
  209.    {
  210.  
  211.     printf("\nEnter Message ( .QUIT to quit)\n>> ");
  212.     fflush(stdout);
  213.  
  214.         /* We do the following ifdef because a Borland compiled
  215.            program returns semaphore locking errors if you kill
  216.            the thread with a pending gets(), and IBM CSET traps
  217.            the entire session if you do a cgets from within a
  218.            thread.  Besides, you're going to put a real interface
  219.            on this anyway, right?                */
  220.   #if defined(CSET2)
  221.     gets((char *)(((SPXInfoStruct *)sendInfo)->spxBuffer));
  222.   #else
  223.     tempspxBuffer[0] = MAX_MESSAGE_SIZE;
  224.     tempspxBuffer[1] = 0;
  225.     cgets(tempspxBuffer);
  226.     strcpy((char *)(((SPXInfoStruct *)sendInfo)->spxBuffer), &tempspxBuffer[2]);
  227.   #endif
  228.  
  229.     if(stricmp((const char *)(((SPXInfoStruct *)sendInfo)->spxBuffer), ".QUIT")==0)
  230.     {
  231.       terminateConnection = TRUE;
  232.       _endthread();
  233.     }
  234.  
  235.    ccode = SEMCALLSET( &(((SPXInfoStruct *)sendInfo)->semaph) );
  236.     if(ccode != 0)
  237.     {
  238.       printf( "Error: SEMCALLSET returned 0x%04X - %d in clientSend\n", ccode,ccode );
  239.       terminateConnection = TRUE;
  240.       _endthread();
  241.      }
  242.  
  243.     ccode = SpxSendSequencedPacket(((SPXInfoStruct *)sendInfo)->connectionNumber, &(((SPXInfoStruct *)sendInfo)->spxEcb));
  244.     if(ccode != 0)
  245.     {
  246.       printf( "Error: SpxSendSequencedPacket returned 0x%04X\n", ccode );
  247.       terminateConnection = TRUE;
  248.       _endthread();
  249.     }
  250.     ccode = CheckForCompletion(&(((SPXInfoStruct *)sendInfo)->spxEcb));
  251.     if(ccode)
  252.     {
  253.       terminateConnection = TRUE;
  254.       _endthread();
  255.     }
  256.    }while(!terminateConnection);
  257.    _endthread();
  258. }
  259.  
  260.  
  261. /* --------------------------------------------------------------
  262.     Generic SPX receive thread that both client and server
  263.     sides use.  It's pretty basic right now, it just gets
  264.     the data and prints it out.
  265.    -------------------------------------------------------------- */
  266.  
  267. void Rec(void *recInfo)
  268. {
  269.  
  270.    WORD        ccode = 0;
  271.  
  272.    ((SPXInfoStruct *)recInfo)->semaph = 0L;
  273.    ((SPXInfoStruct *)recInfo)->spxEcb.hsem = &(((SPXInfoStruct *)recInfo)->semaph);
  274.    ((SPXInfoStruct *)recInfo)->spxEcb.fragCount = 2;
  275.    ((SPXInfoStruct *)recInfo)->spxEcb.fragList[0].fragAddress = &((SPXInfoStruct *)recInfo)->spxHeader;
  276.    ((SPXInfoStruct *)recInfo)->spxEcb.fragList[0].fragSize = sizeof(SPX_HEADER);
  277.    ((SPXInfoStruct *)recInfo)->spxEcb.fragList[1].fragAddress = ((SPXInfoStruct *)recInfo)->spxBuffer;
  278.    ((SPXInfoStruct *)recInfo)->spxEcb.fragList[1].fragSize = MAX_MESSAGE_SIZE;
  279.  
  280.    do
  281.    {
  282.     ccode = SEMCALLSET( &((SPXInfoStruct *)recInfo)->semaph );
  283.     if(ccode != 0)
  284.     {
  285.       printf( "Error: SEMCALLSET returned 0x%04X - %d in clientRec\n", ccode,ccode );
  286.       terminateConnection = TRUE;
  287.       _endthread();
  288.     }
  289.  
  290.     ccode = SpxListenForConnectionPacket( ((SPXInfoStruct *)recInfo)->connectionNumber,&((SPXInfoStruct *)recInfo)->spxEcb);
  291.     if(ccode != 0)
  292.     {
  293.       printf( "Error: SpxListenForConnectionPacket returned 0x%04X\n", ccode );
  294.       terminateConnection = TRUE;
  295.       _endthread();
  296.      }
  297.  
  298.     ccode = CheckForCompletion(&((SPXInfoStruct *)recInfo)->spxEcb);
  299.     if(ccode)
  300.     {
  301.       terminateConnection = TRUE;
  302.       _endthread();
  303.     }    printf( "Data Received <%s>\n>>", ((SPXInfoStruct *)recInfo)->spxBuffer );
  304.     fflush(stdout);
  305.  
  306.    }while(!terminateConnection);
  307.  
  308.    _endthread();
  309.  
  310. }
  311.  
  312.  
  313.  
  314.  
  315.  
  316. /* ------------------------------------------------------------
  317.     This is the client portion of the test code.  It
  318.     calls SpxEstablishConnection, which relies on the
  319.     server side doing an SpxListenForConnection.
  320.    ------------------------------------------------------------ */
  321.  
  322. int clientProc(IPXAddress *tempAddress, USHORT *connectionNumber, void NWPTR *semaphore, SPX_ECB *spxEcb, SPX_HEADER *spxHeader,USHORT *spxSocket)
  323. {
  324.  
  325.    WORD         ccode ;
  326.  
  327.  
  328.    memset(spxHeader, 0, sizeof(spxHeader));
  329.    memset(spxEcb, 0, sizeof(spxEcb));
  330.    memcpy((void *)&(spxHeader->destNet), (const void *)tempAddress,10);
  331.  
  332.    spxHeader->destSocket = NWWordSwap(MY_SOCKET);
  333.    spxHeader->dataStreamType = 0 ;
  334.  
  335.    *semaphore = 0L ;
  336.    spxEcb->hsem = semaphore ;
  337.    spxEcb->fragCount = 1;
  338.    spxEcb->fragList[0].fragAddress = spxHeader ;
  339.    spxEcb->fragList[0].fragSize = sizeof(SPX_HEADER);
  340.  
  341.  
  342.    *spxSocket = 0 ;
  343.    ccode = SpxOpenSocket( spxSocket ) ;
  344.    if(ccode)
  345.    {
  346.     printf( "Error: SpxOpenSocket returned 0x%04X\n", ccode );
  347.     return(1);
  348.    }
  349.    else    printf( "Opened socket 0x%04X\n", *spxSocket );
  350.    ccode = SpxECBErrorCheck(ECB_ERROR_CHECK_ON);
  351.    if(ccode)
  352.    {
  353.     printf( "Error: SpxECBErrorCheck returned 0x%04X\n", ccode );
  354.     return(1);
  355.    }
  356.  
  357.    ccode = SEMCALLSET( semaphore );
  358.    if(ccode)
  359.    {
  360.     printf( "Error: SEMCALLSET returned 0x%04X - %d before establishConnection\n", ccode,ccode );
  361.     return(1);
  362.    }
  363.  
  364.    ccode = SpxEstablishConnection(*spxSocket,spxEcb, 0, WATCHDOG_CONNECTION, connectionNumber ) ;   if(ccode)
  365.    {
  366.     printf( "Error: SpxExtablishConnection returned 0x%04X\n", ccode );
  367.     return(1);
  368.    }
  369.  
  370.    printf("Establishing Connection.....\n");
  371.    ccode = CheckForCompletion(spxEcb);
  372.     if(ccode)
  373.     {
  374.       terminateConnection = TRUE;
  375.       return(1);
  376.     }
  377.    printf( "CONNECTION ESATBLISHED\n") ;
  378.    return 0;
  379.   } // end of sendThread
  380.  
  381.  
  382. /* ------------------------------------------------------------
  383.     This is the server portion of the test code.  It
  384.     calls SpxListenForConnection, which responds when the
  385.     client does a SpxEstablishConnection.
  386.    ------------------------------------------------------------ */
  387.  
  388. int serverProc(USHORT *connectionNumber,void NWPTR *semaphore, SPX_ECB *spxEcb, SPX_HEADER *spxHeader,USHORT *spxSocket)
  389. {
  390.  
  391.  
  392.   WORD     ccode ;
  393.  
  394.    *semaphore = 0L ;
  395.    spxEcb->hsem = semaphore ;
  396.    spxEcb->fragCount = 1;
  397.    spxEcb->fragList[0].fragAddress = spxHeader ;
  398.    spxEcb->fragList[0].fragSize = sizeof(SPX_HEADER);
  399.  
  400.  
  401.    ccode = SpxECBErrorCheck(ECB_ERROR_CHECK_ON);
  402.    if( ccode != 0 )
  403.    {
  404.     printf( "\nError: SpxECBErrorCheck returned 0x%04X", ccode );
  405.     return(1);
  406.    }
  407.  
  408.    *spxSocket = NWWordSwap(MY_SOCKET);
  409.  
  410.    ccode = SpxOpenSocket( spxSocket ) ;
  411.    if( ccode != 0 )
  412.    {
  413.     printf( "\nError: SpxOpenSocket returned 0x%04X", ccode );
  414.     return(1);
  415.    }
  416.    else    printf("Socket 0x%04X opened\n", NWWordSwap(*spxSocket));
  417.  
  418.    ccode = SEMCALLSET( semaphore );
  419.    if( ccode != 0 )
  420.    {
  421.     printf( "\nError: SEMCALLSET returned 0x%04X", ccode );
  422.     return(1);
  423.    }
  424.  
  425.    ccode = SpxListenForConnection(*spxSocket, spxEcb, 0, WATCHDOG_CONNECTION, connectionNumber ) ;
  426.    if( ccode != 0 )
  427.    {
  428.     printf( "\nError: SpxListenForConnection returned 0x%04X", ccode );
  429.     return(1);
  430.    }
  431.    else    printf("Listening for Connection.....\n");
  432.  
  433.   ccode = CheckForCompletion(spxEcb);
  434.     if(ccode)
  435.     {
  436.       terminateConnection = TRUE;
  437.       return(1);
  438.     }   printf( "CONNECTION ESTABLISHED\n") ;
  439.  
  440.    return 0;
  441.   }
  442.  
  443.  
  444.  
  445.  
  446. /* --------------------------------------------------------
  447.     Terminate connection in an error condition
  448.    -------------------------------------------------------- */
  449.  
  450. void  killConn(SPX_ECB *spxEcb, void NWPTR sem, USHORT connectionNumber, USHORT spxSocket)
  451. {
  452.     WORD ccode=0;
  453.  
  454.     //spxEcb->fragCount = 1;
  455.     if(!wasKilled)
  456.     {
  457.        ccode = SEMCALLSET(&sem);
  458.        if( ccode != 0 )
  459.        {
  460.         printf( "Error: SEMCALLSET returned %d in killConn\n",ccode );
  461.         ccode = SpxCloseSocket(spxSocket);
  462.         return;
  463.        }
  464.  
  465.        ccode = SpxTerminateConnection( connectionNumber, (SPX_ECB NWPTR)spxEcb );
  466.        if( ccode != 0 )
  467.        {
  468.         printf( "Error: SpxTerminateConnection returned 0x%04X\n", ccode );
  469.         ccode = SpxCloseSocket(spxSocket);
  470.         return;
  471.        }
  472.  
  473.        ccode = CheckForCompletion(spxEcb);
  474.        if(ccode)
  475.        {
  476.         terminateConnection = TRUE;
  477.         ccode = SpxCloseSocket(spxSocket);
  478.         return;
  479.        }
  480.     }
  481.     ccode = SpxCloseSocket(spxSocket);
  482.     return;
  483. }
  484.  
  485. /*  ----------------------------------------------------------------------
  486.       CheckForCompletion - this function checks the completion status of the
  487.       ECB. It uses a semaph to loop on the status field until the ECB
  488.       has completed. It then checks if that status is an error.
  489.     ---------------------------------------------------------------------- */
  490.  
  491.  
  492. int CheckForCompletion( SPX_ECB *endpoint )
  493. {
  494.     WORD ccode = 0 ;
  495.  
  496.     /* If we have already completed, return */
  497.     if( endpoint->status == SPX_SUCCESSFUL )
  498.         return(0);
  499.  
  500.     /* Check to see if we completed but have an error */
  501.     if(endpoint->status >= ERROR_COMPLETION )
  502.     {
  503.        switch(endpoint->status)
  504.        {
  505.          case 0xA3EC:  printf("Connection Was Terminated\n");
  506.                wasKilled = TRUE;
  507.                break;
  508.          case 0xA3ED:  printf("Connection Was Aborted\n");
  509.                wasKilled = TRUE;
  510.                break;
  511.          case 0xA2EE:  printf("Connection Not Found\n");
  512.                break;
  513.          case 0xA2FC:  printf("Packet Was Canceled\n");
  514.                break;         default:      printf( "\nError: ECB Status returned 0x%04X", endpoint->status );
  515.                break;
  516.        }
  517.        return(1);
  518.     }
  519.  
  520.     if((ccode = SEMCALLWAIT( endpoint->hsem, SEM_INDEFINITE_WAIT )) != 0)
  521.     {
  522.         printf( "\nError: SEMCALLWAIT returned %d in CheckForCompletion",ccode );
  523.         return(1);
  524.     }
  525.  
  526.     /* Check to see if we have an error */
  527.     if(endpoint->status >= ERROR_COMPLETION )
  528.     {
  529.        switch(endpoint->status)
  530.        {
  531.          case 0xA3EC:  printf("Connection Was Terminated\n");
  532.                wasKilled = TRUE;
  533.                break;
  534.          case 0xA3ED:  printf("Connection Was Aborted\n");
  535.                wasKilled = TRUE;
  536.                break;
  537.          case 0xA2EE:  printf("Connection Not Found\n");
  538.                break;
  539.          case 0xA2FC:  printf("Packet Was Canceled\n");
  540.                break;
  541.          default:      printf("\nError: Second ECB Status returned 0x%04X", endpoint->status );               break;
  542.        }
  543.        return(1);
  544.     }
  545.  
  546.     return(0);
  547. }
  548.  
  549.  
  550.  
  551.  
  552. void main(USHORT argc, BYTE *argv[])
  553. {
  554.  
  555.    int            ccode=0;
  556.    int             i;
  557.    IPXAddress       tempAddress;
  558.    TID            tempThreadId, sendThreadId, recThreadId;
  559.    APIRET        rcode;
  560.    USHORT        connectionNumber ;
  561.  
  562.    void NWPTR         semaphore;
  563.    SPX_ECB        spxEcb;
  564.    SPX_HEADER       spxHeader;
  565.    USHORT           spxSocket ;
  566.  
  567.    SPXInfoStruct    sendInfo;
  568.    SPXInfoStruct    recInfo;
  569.  
  570.  
  571.  
  572.  
  573.  
  574.     memset((void *)&sendInfo,0,sizeof(SPXInfoStruct));
  575.     memset((void *)&recInfo,0,sizeof(SPXInfoStruct));
  576.  
  577.  
  578.     if ( argc < 2)
  579.     {
  580.       usage();
  581.       exit( 1 );
  582.     }
  583.     if( stricmp((const char *)argv[1],"CLIENT")==0)
  584.     {
  585.       if(argc < 3 || ! ParseAddress((char *)argv[ 2 ], &tempAddress ))
  586.       {
  587.         usage();
  588.         exit( 1 );
  589.       }
  590.       ccode=clientProc(&tempAddress,&connectionNumber,&semaphore, &spxEcb, &spxHeader,&spxSocket);
  591.       if(ccode)
  592.       {
  593.         exit(1);
  594.       }
  595.     }
  596.     else if(strcmpi((const char *)argv[1],"SERVER")==0)
  597.     {
  598.       ccode=serverProc(&connectionNumber,&semaphore, &spxEcb, &spxHeader,&spxSocket);
  599.       if(ccode)
  600.       {
  601.         exit(1);
  602.       }
  603.     }
  604.     else
  605.     {
  606.       usage();
  607.       exit( 1 );
  608.     }
  609.  
  610.    recInfo.connectionNumber = connectionNumber;
  611.    sendInfo.connectionNumber = connectionNumber;
  612.  
  613.  
  614.    #if (defined(CSET2))
  615.      if ((sendThreadId=_beginthread(Send, NULL, 4096,(void *)&sendInfo)) < 0)
  616.    #else
  617.      if ((sendThreadId=_beginthread(Send, 4096, (void *)&sendInfo)) < 0)
  618.    #endif
  619.      {
  620.        printf("Unable to create Send thread, errno = %d\n",errno);
  621.        exit(1);
  622.      }
  623.    #if (defined(CSET2))
  624.      if ((recThreadId=_beginthread(Rec, NULL, 4096,(void *)&recInfo)) < 0)
  625.    #else
  626.      if ((recThreadId=_beginthread(Rec, 4096, (void *)&recInfo)) < 0)
  627.    #endif
  628.      {
  629.       printf("Unable to create Receive thread, errno = %d\n",errno);
  630.       exit(1);
  631.      }
  632.  
  633.     tempThreadId = 0;
  634.     rcode = DosWaitThread(&tempThreadId, DCWW_WAIT);
  635.     if(rcode)
  636.     {
  637.     printf( "Error: DosWaitThread returned %d\n", rcode );
  638.     exit(1);
  639.     }
  640.  
  641.     printf("\nTerminating this session...\n");
  642.            /* The following code gets around a little problem.
  643.         The receive thread probably has an SpxListenForConnectionPacket
  644.         posted.  If it does, we can't simply kill the thread, SPX
  645.         would be left in an unstable state.  If we simply terminate
  646.         the connection, we may have to wait a number of seconds for
  647.         the SPX watchdog to time out.  So, we terminate the session,
  648.         then cancel any waiting ECB's.  That let's the app terminate
  649.         quickly and safely, and also notifies the other side we are
  650.         shutting down.                        */
  651.     killConn(&spxEcb, semaphore, connectionNumber, spxSocket);
  652.     if(tempThreadId == sendThreadId)
  653.     {
  654.       ccode=SpxCancelPacket(&(recInfo.spxEcb));
  655.       if(ccode)
  656.       {
  657.     printf( "Error: SpxCancelPacket returned %d\n", ccode );
  658.       }
  659.       rcode=DosWaitThread(&recThreadId,DCWW_WAIT);
  660.       if(rcode)
  661.       {
  662.     printf( "Error: DosWaitThread returned %d\n", rcode );
  663.     exit(1);
  664.       }
  665.     }
  666.     else
  667.     {
  668.          /* We're able to just kill the Send thread since it's almost always
  669.            sitting at the gets or cgets command.            */
  670.       rcode=DosKillThread(sendThreadId);
  671.       if(rcode)
  672.       {
  673.     printf( "Error: DosKillThread returned %d\n", rcode );
  674.     exit(1);
  675.       }
  676.     }
  677.  }
  678.  
  679.  
  680.  
  681.