NanoTst
Nano Test Main maTcp.c maTcp.h maTcpLoc.h

I wrote this little program to test Nanos with an ethernet shield.   It uses my infrastructure with an additional lib for Multiple Architecture Networking (maTcp.h).   The MultiArch stuff was written when I was talking between my AMD Ryzen Linux workstation and an Rpi3.  
It formats up an all ASCII message then sends it, via Tcp to the Gateway (Nano).   When the response returns, it displays the response on the console.  

The Multi Architecture TCP package is patterend after Inf drivers.   It uses the same type control blocks etc.   It also uses non-blocking socket I/O and the Inf's ioWait() and ioRegWat(), ioSetWatOut(), and ioClrWatOut() calls.
I didn't spend any time cleaning this up so it may have a little ugliness.      

05/28/22: Page Origin
nanoTst.c
/*H**********************************************************************
* Comm with Nano via Ethernet
*H**********************************************************************/
#include    "inf.h"
#include    "maTcp.h"
#include    "includes.h"

// ================= DEFINES ===============================
#define  RDBUFSIZ   8192
#define  NL         '\n';

// ================ PROTOTYES ==============================
static int reqClt( char *buf );
static int init( int argc, char *argv[] ); // "shopGwy envDev port"

// ============= GLOBAL VARIABLES ===========================
static int      Waitim =2000, CltHndl, Port;
static char     *RmtDev = "nano.0:4001";  // "nano.0:80";  // "host:port"

/***********************************************************************
*DESC:
*RTRN:
***********************************************************************/
static int
init( int argc, char *argv[] )
{
    ioWatInit( argc, argv );                     // INITIALIZE INF's Io Wait
    return( OK );
}
/***********************************************************************
*DESC:
*RTRN:
***********************************************************************/
int
main( int argc, char *argv[] ) 
{
    int      len;
    char    *msg, *resp, msgBuf[STRMAX], rmtHost[STRMAX];

    init( argc, argv );
    utSetLvl( NULL, "55" ); // YOU CAN: export TRC_ENA=55 overrides utSetLvl
    while( 1 )
    {
        utTrc(25,"main: about to open\n");
        if( (CltHndl = ipOpnClt( RmtDev )) <0 )          // OPEN CONNECTION
            utFatal( "ipOpnClt failed");
        utTrc(25,"main: opened OK, Hndl: %d\n", CltHndl);
        sprintf( msgBuf, "ENV\n" );             // FORMAT REQUEST ASCII MSG
        len = reqClt( msgBuf );                    // ENV REQUEST TO SERVER
        utTrc( 25,"main, reqClt rtnd %d\n", len);
        utTrc( 25,"Rcvd: [%s], dLen:%d", msgBuf, len );
        printf( "Rcvd: [%s]\n", msgBuf );                // PRINT RETURN MSG
          ipClose( CltHndl );                              // CLOSE CONNECTION
        utTrc( 25,"about to wait" );
        if( strncmp( msgBuf, "ERR", 3 ))
            utTrcErr("Bad Response: [%s]", msgBuf); 
        ioWait( 1000 );
    }
}
/***********************************************************************
*DESC: SEND/RECEIVE MSG TO ENV SERVER
*RTRN: DLEN else ERR
***********************************************************************/
static int
reqClt( char *mp )
{
    int    rxCnt =0;
    ulong  end;
    char  *cp;

    utTrc( 25,"Bgn" );
    printf( "reqClt: sending [%s]\n", mp );
    ipWt( CltHndl, mp, 4 );                       // SEND REQUEST TO GATEWAY
    *mp =0;                                       // CLEAR (TRUNCATE) BUFFER
    utTrc( 25,"Tx OK, About to enter while" );
    while( 1 ) 
    {                                                   // WAIT FOR RESPONSE
        utTrc( 25,"Tx OK, About to wait for response" );
        if( ioWait( 400 ) == OK ) 
        {                                      // WAIT FOR MESSAGE TO RETURN
            if( (rxCnt = ipRd( CltHndl, mp )) <= 0 )                 // READ
                return( utTrcErr("Srv Rd Err or Timout, RxCnt: %d", rxCnt));
            if( cp = fndChr( mp, 10 ))                     // FIND NL AT END
                *cp = ']';              // REPLACE NL WITH RT BRKT FOR PRINT
            utTrc( 25,"RdCmplt, Len: %d, [%s", rxCnt, mp );
            break;
        }
          else
        {
            utTrcErr("Timeout");
              break;
        }
    }
//    ipClose( CltHndl );
    utTrc( 25,"End, Len: %d, [%s]", rxCnt, mp );
    return( rxCnt );
}
maTcp.c
/*******************************************************************
* USES TCP SOCKETS TO TRANSFER ASCII MSGS TO OTHER PROCESSES
* ASCII MSGES ONLY, ASSUMES CLIENT HAVE ONE CONNECTION TO ONE SERVER
* SERVERS MAY HAVE MANY CONECTIONS
**********************************************************************/
#include    <stdio.h>    
#include    <errno.h>
#include    <stdlib.h>    
#include    <string.h>
#include    <stdint.h>
#include    <unistd.h>    
#include    <time.h>
#include    <sys/types.h>    
#include    <sys/socket.h>
#include    <linux/socket.h>
#include    <netinet/tcp.h>    
#include    <netinet/in.h>    
#include    <netdb.h>
#include    "inf.h"
#include    "maTcp.h"
#include    "maTcpLoc.h"
#include    "includes.h"

/**************************** DEFINES ***********************************/
#define   MAXPORTS    10
#define   RDSIZ       8192
typedef struct   sockaddr_in  sock_t; //   from:  netinet/in.h
#define   SET_NOBLOCK(fd)    ioctl(fd,FIONBIO,&NoBlk)
#define   RDBUFSIZ    8192
typedef unsigned long long ull;

/**************************** VARIABLES ***********************************/
pcb_t        *Ports[MAXPORTS];
static int    NoBlk = 1;                    // USED WHEN SET_NOBLOCK, CALLED
extern int    h_errno;

/**************************** PROTOTYPES ***********************************/
static int     ipAccept( ccb_t *cp );
static ccb_t  *ipAddCcb( pcb_t *pp );
static iob_t  *ipAddIob( iob_t **ipp, int len );
static pcb_t  *ipAddPort( char *dev, int flg );
static int     tcpAddr( ccb_t *cp, char *host, short port );
static ccb_t  *ipConn( pcb_t *pp );
static int     ipDconn( ccb_t *cp );
static int     ipDoRsts( ccb_t *cp );
static int     ipFreeCcb( ccb_t *cp );
static int     ipFreeIob( iob_t **ip );
static int     ipFreePcb( pcb_t *pp );
static int     ipRdCplt( ccb_t *cp );
static int     ipRdErr( ccb_t *cp, int rtn, int  eno );
static int     rdIrq();
static int     tcpGetSd();
static int     wtIrq();


/*F**********************************************************************
* DESC: OPEN IP CLIENT PORT
* RTRN: PORT HANDLE NBR, ELSE ERR
*F**********************************************************************/
int                   // "Host:Port"   ("WxIfc.ary.loc:6100")
ipOpnClt( char *dev )   // FLG = IP_CLT
{              // CLIENT SHOULD GO AHEAD AND CONNECT TO SERVER
    int        sd, pHndl;
    pcb_t     *pp;
    ccb_t     *cp;

    utTrc( 50,"Bgn, dev %s", dev );
    if( (pp = ipAddPort( dev, IP_CLT )) <=0)             // NEW PORT CTL BLK
        return( utTrcErr("ipAddPort failed"));
    pHndl = pp->pNdx;
    pp->flags |= IP_CLT;                            // SET PCB's CLIENT FLAG
    if( !(cp = ipConn( pp )))
        return( utTrcErr("ipConn failed"));
    utTrc( +50,"End OK, sd %d", sd );
    return( pHndl );
}
/*F**********************************************************************
* DESC: OPEN SERVER
* RTRN: PORT HANDLE NBR, ELSE ERR
*F**********************************************************************/
int                   // "Host:Port"   ("WxIfc.ary.loc:6100")
ipOpnSrv( char *dev ) // FLG = IP_SRV
{
    int        sd, pHndl, rtn;
    char       error[80];
    pcb_t     *pp;
    ccb_t     *cp;

    utTrc( +50,"Bgn, dev %s", dev );
    if( (pp = ipAddPort( dev, IP_SRV )) <=0 )
        return( utTrcErr("ipAddPort failed"));
    pHndl = pp->pNdx;
    cp = ipAddCcb( pp );                    // CREATE INITIAL CONN CTRL BLK
    cp->sd = sd = tcpGetSd();                                // GET A SOCKET
    cp->pNdx = pp->pNdx;
    cp->in = ipAddIob( &cp->in, RDSIZ );               // CREATE INITIAL IOB
    utTrc( +20,"Host: %s, Port %d", pp->host, pp->hPort );
    if( tcpAddr( cp, pp->host, pp->hPort ) != OK )         // ADDRESS SOCKET
        return( utTrcErr("tcpAddr error"));
    if( (rtn = bind( cp->sd, (struct sockaddr*)&cp->sock, cp->sockSiz)) <0)
        return( utTrcErr( "[%s] bind error: %d", pp->host, errno), 0-errno);
    if( listen( cp->sd, 8 ) < 0 )                 // LISTEN FOR CONNECTIONS
        return( utTrcErr("[%s] listen error", pp->host ));
    SET_NOBLOCK( sd );
    ioRegWat( sd, rdIrq, wtIrq, NULL, (void*)cp );
    utTrc( +50,"End OK, sd %d", sd );
    return( rtn );
}
/*F**********************************************************************
* DESC: CLOSE PORT, CLEAN UP
* RTRN:
*F**********************************************************************/
int
ipClose( int pHndl )
{        
    pcb_t    *pp;

    utTrc( 50, "Bgn");
    if( !(pp = Ports[pHndl]))
        return( utTrcErr("Bad Handle: [%d]", pHndl));
    if( pp->cp )
        ipFreeCcb( pp->cp );
    ipFreePcb( pp );
    utTrc( 50, "End");
    return( OK );
}
/*F**********************************************************************
* DESC: Rcv data from host, 52us+ / char
* RTRN: LEN ELSE ERR
*F**********************************************************************/
int
ipRd( int pHndl, uchar *msg )
{
    int        rtn;
    pcb_t     *pp;
    ccb_t     *cp;
    iob_t     *ip;
    
    utTrc( +50,"Bgn");
    if( !(pp = Ports[pHndl]))                           // GET PORT CTRL BLK
        return( utTrcErr("Bad Handle [%d]", pHndl));
    cp = pp->cp;
    if( !(ip = cp->in) )                // GET INP IOB PTR FROM CONN CTL BLK
        ip = ipAddIob( &cp->in, RDSIZ );
    if( pp->flags & IP_CLT )
        ip->state =1;           // PREVENT CLIENT FROM ACCEPTING CONNECTIONS
    if( !(ip->flags & IP_CMPLT))
        return( utTrc( 50,"Not Cmplt"),NOTFND );
    utTrc( +50,"Read Complete: %d", ip->totCnt );
    rtn = ip->totCnt - HDRSIZ;
    ip->msg[rtn -1] =0;
    strcpy( (char*)msg, ip->msg );
    ipFreeIob( &cp->in );
    utTrc( 50, "End: [%d], dLen:%d", pHndl, cp->dLen );
    return( rtn );
}
/*F**********************************************************************
* DESC: INITIATE AND SEND DATA TO ANOTHER PROCESS
* RTRN: CUR CNT ELSE ERR
*F**********************************************************************/
int
ipWt( int pHndl, uchar *msg, int len )
{                                            // SEND DATA TO ANOTHER PROCESS
    int        rtn;
    uchar     *wtP, *ptr;
    pcb_t     *pp;
    ccb_t     *cp;
    iob_t     *ip;
    
    utTrc( +50,"Bgn, len: %d", len);
    if( !(pp = Ports[pHndl]))
        return( utTrcErr("Bad Handle [%d]", pHndl));
    if( !(cp = pp->cp))
        cp = ipConn( pp );    
    ip = ipAddIob( &cp->out, HDRSIZ + len );            // GET OUTPUT BUFFER
    if( (pp->flags & IP_CLT) || (pp->flags & IP_CONND) )
        ip->state = 1;
    ip->curCnt = 0;                              // CLEAR CURRENT READ COUNT
    ip->totCnt = HDRSIZ + len;
    memcpy( ip->msg, msg, len );
    sprintf( ip->hdr, "%04d", len );
    ioSetWatOut( cp->sd );             // ARM INTERRUPT, THUS START TRANSFER
    while( !(ip->flags & IP_CMPLT) )
        ioWait( 100 );                            // WAIT FOR WT TO COMPLETE
    rtn = ip->curCnt;
    ipFreeIob( &cp->out );
    utTrc( +52,"END OK, wrote:%d", rtn);
    return( rtn );
}
/*F*********************************************************************
* DESC: SERVER ACCEPT CONNECTION
* RTRN: CUR CNT, ELSE ERR
*F*********************************************************************/
static int
ipAccept( ccb_t *cp )
{
    int         sd;
    pcb_t      *pp;
    ccb_t      *cp2;
    iob_t      *ip;

    utTrc( 50, "Bgn");
    pp = cp->pp;
    if( !(pp->flags & IP_SRV) )
    {                                     // ONLY SERVERS ACCEPT CONNECTIONS
        utTrcErr( "Accept on Client?");
        return( ipDconn( cp ));
    }
    if((sd = accept( cp->sd, (void*)&cp->sock, (socklen_t*)&cp->sockSiz))<0)
    {
        if( errno ==  EINVAL )
            ipDconn( cp );
        return( utTrcErr( "accept Error eno:%d", errno ));
    }
    utTrc( +50,"Sd:%d, %s", sd, cp->pp->host );
    if( (cp2 = ipAddCcb( pp )) <=0 )     // CREATE NEW CONN CTL FOR NEW CKT
    {
        close( sd );
        ipDconn( cp );
        return( utTrcErr( "ipAddCcb Err") );
    }
    cp2->sd = sd;
    SET_NOBLOCK( sd );
    ip = ipAddIob( &cp2->in, RDSIZ );
    ip->state =1;
    pp->flags |= IP_CONND;
    ioRegWat( sd, rdIrq, wtIrq, NULL, (void*)cp2 );
    utTrc( +50,"End, Sd:%d OK", cp2->sd );
    return( NOTFND );
}
/*F**********************************************************************
* DESC: CLOSE & RE-OPEN IP PORT
* RTRN:
*F**********************************************************************/
int
ipRstrt( int pHndl )
{        
    int      hndl;
    pcb_t   *pp;

    utTrc( 50, "Bgn");
    if( !(pp = Ports[pHndl]) )
        return( utTrcErr("Bad port handle [%d]", pHndl));
    ipClose( pHndl );
    if( pp->flags & IP_CLT )
        hndl = ipOpnClt( pp->dev );
    else
        hndl = ipOpnSrv( pp->dev );
    utTrc( 50, "End");
    return( hndl );
}
// ***************** ALL STATIC FUNCS BEYOND THIS POINT *******************
/*F**********************************************************************
* DESC: READ INTRPT CALL-BACK  rdIrq DOES ALL IN-BOUND XFER DATA WORK 
* RTRN:
*F**********************************************************************/
static int  // SHOULD READ HDR INTO ccb'S HDR1, THEN CREATE NEW IOB
rdIrq( ccb_t *cp )      // ipRd SHOULD FREE IOB AFTER SOPYING IT"S DATA
{
    int       rtn, rdCnt, siz;
    uchar    *rdp, hdr[16];;
    iob_t    *ip;

    if( !(ip = cp->in))
        ip = cp->in = ipAddIob( &cp->in, RDSIZ );  
    if( cp->pp->flags & IP_CLT )
        ip->state = 1;
    utTrc( +52,"Begin, sock: %d, State: %d", cp->sd, ip->state);
    while( 1 )
    {
        switch( ip->state )
        {
            case 0:                                           // UNCONNECTED
                if( cp->pp->flags & IP_CONND )
                {
                    ip->state = 1;
                    break;
                }
                if( cp->pp->flags & IP_SRV )
                    return( ipAccept( cp ));       // ACCECPT NEW CONNECTION
            utTrc( 40,"State:0, pp->flags: 0X%0x NOT SUPPOSED TO GET HERE!"
                , cp->pp->flags);
                ipDconn( cp );
                break;
            case 1:                                            // BGN HDR RD
                ip->totCnt = HDRSIZ;
                ip->curCnt = 0;
                ip->state++;                           // MOVE TO NEXT STATE
                break;
            case 2:                              // HDR RD CMPLT, BGN RD MSG
                memcpy( hdr, ip->hdr, 4 );                // CPY XMITTED LEN
                  hdr[4] = 0;                           // MAKE SURE ITS A STR
                siz = atoi( hdr );                           // GET MSG LEN
                if( siz > (RDSIZ - HDRSIZ))
                    return( utTrcErr("Msg too large: %d", siz), ipDconn(cp));
                ip->totCnt += siz;
                ip->curCnt = HDRSIZ;                           // SKIP HEADER
                ip->state++;                            // MOVE TO NEXT STATE
                break;
            case 3:                                                // RD MSG
                if( ip->curCnt >= ip->totCnt )
                    return( ipRdCplt( cp ));                   // END OF MSG
                break;
        }
        rdCnt = ip->totCnt - ip->curCnt;
        rdp = &ip->hdr[ip->curCnt];
        utTrc( +50,"Recv, sock: %d, rdCnt: %d, curCnt: %d"
            , cp->sd, rdCnt, ip->curCnt );
        if( (rtn = recv( cp->sd, rdp, rdCnt, 0 )) < 0 )
        {
            if( (rtn < 0) && (errno == EAGAIN))
                continue;
            return( ipRdErr( cp, rtn, errno ));// GO FIGURE OUT WHAT ERROR IS
        }
        utTrc( 50,"Recvd [%s]\n", ip->hdr );
        ip->curCnt += rtn;
    }
    utTrc( +52,"BAD End");
    return( OK );
}
/*F**********************************************************************
* DESC: WRITE INTERRUPT CALL-BACK wtIrq DOES ALL OUT_BOUND XFER DATA WORK
* RTRN:
*F**********************************************************************/
static int
wtIrq( ccb_t *cp )
{
    int      ret, wtCnt, eno;
    char    *ptr;
    iob_t   *ip;

    ip = cp->out;
    utTrc( +52,"Bgn, sock: %d", cp->sd );
    wtCnt = ip->totCnt - ip->curCnt;
    ptr = (char*)&ip->hdr[ip->curCnt];
    if( (ret = send( cp->sd, ptr, wtCnt, 0 )) < 0 )
    {
        eno = errno;
        ioClrWatOut( cp->sd );
        return( utTrcErr("Write error, ret: %d, eno:%d", ret, eno));
    }
    if( ret > 0 )
        ip->curCnt += ret;
    if( ip->curCnt == ip->totCnt )
    {
        ioClrWatOut( cp->sd );
        ip->flags |= IP_CMPLT;
        utTrc( +52,"Write End, Len: %d", ip->curCnt );
        return( OK );
    }
    utTrcErr( "Not Found");
    return( NOTFND );
}
/*F*********************************************************************
* DESC: 
* RTRN: 
*F*********************************************************************/
static int
ipRdErr( ccb_t  *cp, int rtn, int eno )
{             // SORT OUT WHAT KIND OF ERROR, AND WHAT TO DO ABOUT IT
    utTrc( 50,"rtn:%d, eno:%d", rtn, eno );
      if((rtn <0) && ((eno == EWOULDBLOCK) || (eno == ENOMEM) || (eno == EAGAIN)
    || (eno == EINTR)) )
        return( utTrc( +50, "Sd:%d Blkd, rtn:%d", cp->sd, rtn), 0);
//    if( eno == EAGAIN )
//        return( 0 );
    if( !rtn || (eno ==ECONNRESET) || (eno ==ECONNREFUSED)
    || (eno ==ENOTSOCK) || (eno ==EPIPE) || (eno ==ENOTCONN) || (eno ==EBADF)
    || (eno ==EINVAL))
        return( utTrcErr("eno:%d, Cnt:%d", eno, rtn), ipDoRsts( cp ));
    utTrc( 50,"Leaving");
    return( ERR );
}
/*F*********************************************************************
* DESC: 
* RTRN: 
*F*********************************************************************/
static int
ipRdCplt( ccb_t  *cp )
{
    iob_t  *ip;

    utTrc( 50,"BGN");
    ip = cp->in;
    ip->flags |= IP_CMPLT;
    cp->dLen = ip->curCnt;
    utTrc( 40,"Rd Cmplt: tot:%d, dLen:%d", ip->totCnt, cp->dLen );
    return( OK );
}
/*F*********************************************************************
* DESC: CONNECT
* RTRN: 
*F*********************************************************************/
static ccb_t*
ipConn( pcb_t *pp )
{
    int      sd, rtn, eno, pHndl;
    char     errBuf[STRMAX];
    ccb_t   *cp;

    utTrc( +50,"Bgn");
    pp->cp = cp = ipAddCcb( pp );           // CREATE INITIAL CONN CTL BLK
    cp->sd = sd = tcpGetSd();                     // GET A SOCKET DESCRIPTOR
    cp->pNdx = pp->pNdx;
    utTrc( +50,"Host: %s, Port %d", pp->host, pp->hPort );
    if( tcpAddr( cp, pp->host, pp->hPort ) != OK )          // SET SOCK ADDR
        return( utTrcErr("tcpAdr Err"), NULL );
    utTrc( +50,"Connecting Sd:%d", sd );
    if( (rtn = connect( sd, (void*)&cp->sock, cp->sockSiz )) != 0)
    {                                                      // CONNECT FAILED
        eno = errno;
        strcpy( errBuf, pp->dev );                   // SAVE PCB "host:port"
        ipClose( pHndl );                                       // CLOSE PCB
        if( (eno == ECONNREFUSED) )
            return( utTrcErr( "Conn refused Host:%s", errBuf ), NULL);
        return( utTrcErr( "connect errBuf %d", eno ), NULL);
    }
    pp->flags |= IP_CONND;                       // SHOW CLIENT AS CONNECTED
    SET_NOBLOCK( sd );
    ioRegWat( sd, rdIrq, wtIrq, NULL, (void*)cp );
    utTrc( +50,"End OK, sd %d", sd );
    return( cp );
}
/*F*********************************************************************
* DESC: DISCONNECT, FREE ccb
* RTRN: 
*F*********************************************************************/
static int
ipDconn( ccb_t *cp )
{
    int        sd, flags;
    pcb_t     *pp;
    ccb_t    **cpp, *cp2;

    sd = cp->sd;
    utTrc( +50,"Bgn Sd:%d", sd );
    pp = cp->pp;
    for( cpp = &pp->cp; (cp2 = *cpp) && (cp2->sd != sd)
        ; cpp = &cp2->link);                     // FIND CP->SD IN PCB CHAIN
    if( !cp2 )
        return( utTrcErr("BAD ccb ptr for Sd:%d", sd));
    *cpp = cp->link;
    ioDregWat( sd );
    close( sd );                         // TELLS ANY CONNECTED HOST TO QUIT
    if( cp->in )
        ipFreeIob( &cp->in );            // FREE ALL MESSAGES IN INPUT QUEUE
    if( cp->out )
        ipFreeIob( &cp->out );          // FREE ALL MESSAGES IN OUTPUT QUEUE
    ipFreeCcb( cp );
    pp->flags &= !IP_CONND;                       // TURN CONNECTED FLAG OFF
    utTrc( +50,"End Sd:%d", sd );
    return( NOTFND );
}
/*F*********************************************************************
* DESC: ADD PORT CTRL BLOCK
* RTRN: PTR TO PCB ELSE NULL, ERR
*F*********************************************************************/
static pcb_t*
ipAddPort( char *dev, int flg )
{
    int        sd, ndx, rtn, sockSiz, eno;
    char      *sp, error[80];
    pcb_t     *pp;
    ccb_t    **cpp, *cp;

    utTrc( +50,"Bgn, dev %s", dev );
    for( ndx =0; (ndx < MAXPORTS) && Ports[ndx] ; ndx++ );// FND UNUSED PORT
    pp = Ports[ndx] = calloc( 1, sizeof( pcb_t ));        // ALLOC A NEW PCB
    pp->pNdx = ndx;                                       // SAVE PORTS[NDX] 
    pp->flags |= flg;
    pp->dev  = strdup( dev );
    pp->host = strdup( dev );
    pp->hdrLen = HDRSIZ;
    sp = zotChr( pp->host, ':' );                             // IP PORT NBR
    pp->hPort = atoi( sp );                     // CONVERT ASCII PORT TO INT
    utTrc( 50, "End, P-%d, 0X%p", ndx, (void*)pp);
    return( pp );
}
/*F*********************************************************************
* DESC: ADD CONNECTION CONTROL BLOCK
* RTRN: PTR TO CCB ELSE ERR         AKA ipAddCcb
*F*********************************************************************/
static ccb_t*
ipAddCcb( pcb_t *pp )
{
    ccb_t   **cpp, *cp, *cp2;

    utTrc( 50, "Bgn");
    cp2 = pp->cp;                                // GET CURRENT CONN CTL BLK
    if( !(cp = calloc( 1, sizeof( ccb_t ))))
        return( utTrcErr("Ccb calloc failed"),(ccb_t*)ERR);
    pp->cp = cp;                         // INSERT NEW CCB TO PP's CCB CHAIN
    if( cp2 )
        cp->link = cp2;       // ADD ORIGINAL CONN CTL BLK TO NEW CCB's TAIL
    cp->sockSiz = sizeof( sock_t );
    cp->pNdx = pp->pNdx;
    cp->pp = pp;
    utTrc( 50, "End, 0X%p", (void*)cp );
    return( cp );
}
/*F*********************************************************************
* DESC: MAKE I/O CNTRL BLCK, IPP POINTS TO CCB IN OR OUT CHAIN
* RTRN: 
*F*********************************************************************/
static iob_t*
ipAddIob( iob_t **ipp, int len )
{
    int      siz;
    ccb_t   *cpp;
    iob_t   *ip, *ip1;

    utTrc( 50, "Bgn");
    siz = sizeof( iob_t ) + len + HDRSIZ;
    if( !(ip = calloc( 1, siz )))
        return( utTrcErr("iob calloc failed"), (iob_t*)ERR);
    if( (ip1 = *ipp))
        ip->link = ip1;                     // PUT NEW IOB IN FRONT OF QUEUE
    *ipp = ip;
    ip->siz = len;                                 // SAVE EXPECTED MSG SIZE
    utTrc( 50, "End, 0X%p", (void*)ip );
    return( ip );
}
/*F*********************************************************************
* DESC: FREE PORT CTRL BLK
* RTRN: 
*F*********************************************************************/
static int
ipFreePcb( pcb_t *pp )
{
    int  pHndl;

    utTrc( 50, "Bgn");
    pHndl = pp->pNdx;
    free( pp->host );
    free( pp->dev );
    free( pp );                                                 // FREE PCB
    Ports[pHndl] = NULL;
    utTrc( 50, "End");
    return( OK );
}
/*F*********************************************************************
* DESC: FREE CONNECTION BLOCK
* RTRN: 
*F*********************************************************************/
static int
ipFreeCcb( ccb_t *cp )
{
    ccb_t    *cp1, *cp2;
    iob_t    *ip1, *ip2;
    
    utTrc( 50, "Bgn");
    ioDregWat( cp->sd );
    close( cp->sd );
    cp->sd =0;
    for( cp1 = cp; cp1 ; cp1 = cp2 )
    {                                               // FREE ANY CONNECTIONS
        cp2 = cp1->link;
        while( cp1->in )
            ipFreeIob( &cp1->in );
        while( cp1->out )
            ipFreeIob( &cp1->out );
        free( cp1 );
    }
    utTrc( 50, "End");
    return( OK );
}
/*F*********************************************************************
* DESC: FREE IO BLOCK ON FRONT OF CHAIN
* RTRN: 
*F*********************************************************************/
static int
ipFreeIob( iob_t **ipp )
{
    iob_t   *ip, *ip1;

    utTrc( 50, "Bgn");
    ip = *ipp;
    *ipp = ip->link;
    free( ip );
    utTrc( 50, "End");
    return( OK );
}
/*F*********************************************************************
* DESC: FREE PORT CTRL BLK
* RTRN: 
*F*********************************************************************/
static int
ipDoRsts( ccb_t *cp )
{

    utTrc( 50,"Rst Sd: %d", cp->sd );
    ipDconn( cp );
//    if( pp->flags & IP_CLT )
//        ipRecon( pp );
    return( ERR );
}
/*F*********************************************************************
* DESC: Get a TCP socket. 
* RTRN: Socket Descriptor (positive integer) else ERR. 
*F*********************************************************************/
static int
tcpGetSd()
{
    int        sd, option;

    utTrc( 50, "Bgn");
    if(( sd = socket( AF_INET, SOCK_STREAM, 0 )) < 0 )
        return( utTrcErr( "socket() error eno:%d", errno ));
    option = RDBUFSIZ;
    setsockopt( sd, SOL_SOCKET, SO_SNDBUF, (char*)&option, sizeof(option));
    setsockopt( sd, SOL_SOCKET, SO_RCVBUF, (char*)&option, sizeof(option));
    option = 1;
    setsockopt( sd, SOL_SOCKET, SO_KEEPALIVE, (char*)&option, sizeof(option));
    setsockopt( sd, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option));
    setsockopt( sd, SOL_TCP,    TCP_NODELAY,  (char*)&option, sizeof(option));
    utTrc( +50,"returning Sd:%d", sd );
    return( sd );
}
/*F*********************************************************************
* DESC: Implements "makSock", Convert an io addr to TCP socket addr.
* RTRN: OK, else Err.
*F*********************************************************************/
static int
tcpAddr( ccb_t *cp, char *host, short port)
{
    char              tmp[STRMAX];
    struct  hostent  *he;                                    // from netdb.h
    sock_t           *sap;

    utTrc( 50, "Bgn");
    sap = (sock_t*)&cp->sock;
    sap->sin_family      = AF_INET;
    sap->sin_port        = htons( port );
    strUcp( tmp, host );
    if( !strcmp( host, "*" ) || !strcmp( tmp, "LOCALHOST" ) )
        sap->sin_addr.s_addr = htonl( INADDR_LOOPBACK );    // FORCE LOOPBACK
    else
    {
        if( !(he = gethostbyname( host )))
            return( utTrcErr( "Can't address sock [%s], Err: %s"
                , host, strerror( errno) ));
        sap->sin_addr.s_addr = ((struct in_addr*)(he->h_addr))->s_addr;
    } // from:   netinet/in.h   h_addr_list[0]
    utTrc( 50, "End");
    return( OK );
}
maTcp.h
/*H**********************************************************************
*
**********************************************************************/
#ifndef  _MA_IP_H
#define  _MA_IP_H

// ***************** PROTOTYPES **************************
int    ipOpnClt( char *dev );                // OPEN CLIENT, RTN PORT HANDLE
int    ipOpnSrv( char *dev );                // OPEN SERVER, RTN PORT HANDLE
int    ipWt(  int pHndl, uchar *buf, int len );           // RTN: CURCNT/ERR
int    ipRd(  int pHndl, uchar *buf );                    // RTN: CURCNT/ERR
int    ipClose( int pHndl );                         // RTN: NEW PORT HANDLE
int    ipRstrt( int pHndl );                                      // RTN: OK

#endif     // _MA_IP_H
maTcpLoc.h
#ifndef _MA_IPLOC_H
#define _MA_IPLOC_H
// ============ FLAG BITS ============
#define IP_CLT          0X00000001
#define IP_SRV          0X00000002
#define IP_CONND        0X00000004
#define IP_INIT         0X00000008
#define IP_CMPLT        0X00000010
#define HDRSIZ          8

typedef struct  sockaddr_in  sock_t;

typedef struct iob {                   // INPUT/OUTPUT BUFFER
    int       state;                          // PORT STATE (SEE FLGS ABOVE)
    int       flags;                          // PORT STATE (SEE FLGS ABOVE)
    int       siz;                            // PORT STATE (SEE FLGS ABOVE)
    int       totCnt;                                         // MSG TOT CNT
    int       curCnt;                                        // MSG CURR CNT
    struct ccb  *cp;
    struct iob  *link;
    char      hdr[HDRSIZ];                                    // MESSAGE HDR
    char      msg[4];                                    // TRANSFERRED DATA
    }iob_t;

typedef struct ccb  {             // CONNECTION CONTROL BLOCK
    int       flags;                                      // CONECTION FLAGS
    int       pNdx;                                         // Ports[] INDEX
    int       dLen;                                     // RCV'D DATA LENGTH
    int       sd;                                       // SOCKET DESCRIPTOR
    int       sockSiz;                                        // SOCKET SIZE
    sock_t    sock;                                                // SOCKET
    iob_t    *in;                                   // IN_BOUND BUFFER QUEUE
    iob_t    *out;                                 // OUT_BOUND BUFFER QUEUE
    uchar     hdr1[HDRSIZ];         // BUFF FOR READING MESSAGE HEADER (LEN)
    struct pcb   *pp;                              // PORT CONTROL BLOCK PTR
    struct ccb  *link;                                      // CHAIN OF CCBs
    }ccb_t;

typedef struct pcb {             // PORT CONTORL BLOCK (BOTH LOCAL & REMOTE)
    int       pNdx;                                           // PORTS[] NDX
    int       flags;                                     // FLAGS, SEE ABOVE
    int       hdrLen;                                       // HEADER LENGTH
    short     hPort;                                         // HOST's PORT#
    char     *dev;                                        // DEV "Host:Port"
    char     *host;                                             // HOST NAME
    ccb_t    *cp;                               // CONNECTION CONTROL BLOCK
}pcb_t;

#endif // _MA_IPLOC_H
The Env server's resonse.