Chapter 4. Examples

Table of Contents

4.1. OUTBOUND TCPGW
4.1.1. Environment File
4.1.2. TCPGW
4.1.3. Remote Node
4.1.4. Server
4.2. Synchronous INBOUND TCPGW
4.2.1. Environment File
4.2.2. TCPGW
4.2.3. Remote Node
4.2.4. Client
4.3. Non-blocking TCPGW
4.3.1. Environment File
4.3.2. TCPGW
4.3.3. Remote Node
4.3.4. Server
4.3.5. Client

This chapter describes examples of TCP/IP gateway service types.

4.1. OUTBOUND TCPGW

In this example, TCPGW is started up with Tmax. The remote node sends a request, and TCPGW calls the user-specified service. When the service completes processing the request and sends the result to TCPGW, TCPGW sends the result back to the remote node. This example is not related to TCPGW client/server method. Configure TCPGW by modifying custom.c according to the remote node environment.

The following diagram shows the operation of the OUTBOUND TCPGW program.

[Figure 4.1] OUTBOUND TCPGW Program Operation

OUTBOUND TCPGW Program Operation


The program is composed of the following files.

TypeFile Name
Configuration Filetcpgw.m
TCPGWcustom.h, custom.c
Remote Noderclient.c, custom.h
Serversvr.c

4.1.1. Environment File

<tcpgw.m>

*DOMAIN        
res             SHMKEY = 88000,
                MINCLH = 1,
                MAXCLH = 1,
                TPORTNO = 8888
        
*NODE        
node1           TMAXDIR = "/home/tmaxqas/tmax",
                APPDIR  = "/home/tmaxqas/tmax/appbin",
                PATHDIR = "/home/tmaxqas/tmax/path",
                TLOGDIR = "/home/tmaxqas/tmax/log/tlog",
                ULOGDIR = "/home/tmaxqas/tmax/log/ulog",
                SLOGDIR = "/home/tmaxqas/tmax/log/slog",
        
*SVRGROUP        
svg1            NODENAME = node1
        
*SERVER        
tcpgw           SVGNAME = svg1, 
                MIN = 1, MAX = 1,
                CPC = 5, 
                SVRTYPE = CUSTOM_GATEWAY,
                CLOPT = "-- -P 5050 -N 2 –d 0"
svr             SVGNAME = svg1,
                MIN = 1, MAX = 1
        
*SERVICE        
TOUPPER        SVRNAME = svr

4.1.2. TCPGW

<custom.h>

#ifndef _CUSTOM_H_
#define _CUSTOM_H_

/* -------------------------------------------------------------------- */
/*              Fixed structures and macros       */

#define MSG_MAGIC               "Tmax"
#define REMOTE_REQUEST          0
#define REMOTE_REPLY             1
#define SVC_NAME_LENGTH         20

/* msg_info_t struct must not be not redefined.*/
typedef struct msg_info {
    char  svc[SVC_NAME_LENGTH];     
    int   err;          
    int   len;          
    int   uid;          
    int   flags;        
    int   msgtype;     
    int   channel_id;   
} msg_info_t;

/* -------------------------------------------------------------------- */
/*        Modifiable structures and macros         */
/* msg_header_t and msg_body_t structs can be redefined. */
typedef struct msg_header {
    int         len;            
} msg_header_t;

typedef struct msg_body {
    char        name[16];
    char        data[100];
} msg_body_t;

#endif

<custom.c>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include "custom.h"

/* Since msg_header_size and comm_header_size are used in TCPGW library,
   they must be defined. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* The internal logics do not have to implemented when the following
   functions are not required. */
/* However, they must be defined since they are used in the TCPGW library. */
int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int addr, int type, int fd)
{
    return 1;
}

int get_msg_length(msg_header_t *hp)
{
    if (hp == NULL || hp->len <= 0)
        return -1;
    /* Returns the actual data length. */
    /* Reads the incoming data from the remote node using the return value.  */
    return ntohl(hp->len);
}

int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
    msg_body_t *body;

    if ((info == NULL) || (hp == NULL) || (data == NULL))
        return -1;

    body = (msg_body_t *)data;

    info->len   = ntohl(hp->len);
    info->err   = 0;
    info->flags = 0;

    memset(info->svc, 0x00, SVC_NAME_LENGTH);
    strncpy(info->svc, body->name, 8);

    /* Return REMOTE_REQUEST to indicate that the request is from the remote node. */
    return REMOTE_REQUEST;
}

int get_service_name(char *header, int err, char *svc)
{
    return -1;
}

int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
    msg_body_t *body;

    if ((info == NULL) || (hp == NULL) || (data == NULL))
        return -1;

    hp->len = htonl(info->len);
    body = (msg_body_t *)data;

    /* Send the error status in body->name */
    if (info->err)   /* error */
        strcpy(body->name, "Fail");
    else
        strcpy(body->name, "Success");

    /* Return the data length needed to send result to the remote node. */
    return (info->len + sizeof(msg_header_t));
}

int get_channel_num(char *data)
{
    return -1;
}

int put_msg_complete(char *hp, char *data, msg_info_t *info)
{
    return 1;
}

int remote_closed(int index, int type)
{
    return 1;
}

int prepare_shutdown(int code)
{
    return 1;
}

int set_service_timeout(int uid, char *header)
{
    return 1;
}

int chk_end_msg(int len, char *data)
{
    return len;
}

int outmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

<Makefile>

#This Makefile is for ibm 32bit.
#TARGET must be same as the SERVER name defined in the Tmax environment file.
TARGET  = tcpgw
APOBJS  = $(TARGET).o

#To create TCPGW, libtcpgw.a or libtcpgw.so must be linked.
LIBS    = -ltcpgw -ltmaxgw
OBJS    = custom.o register.o

CFLAGS  = -q32 -O -I$(TMAXDIR) -D_DBG
LDFLAGS = -brtl

APPDIR  = $(TMAXDIR)/appbin
LIBDIR  = $(TMAXDIR)/lib

.SUFFIXES : .c

.c.o:
        $(CC) $(CFLAGS) $(LDFLAGS) -c $<

$(TARGET): $(OBJS)
        $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS)
        mv $(TARGET) $(APPDIR)/.
        rm -f $(OBJS)

$(APOBJS): $(TARGET).c
        $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c

4.1.3. Remote Node

<rclient.c>

The following example is a remote node's client program for testing.

To test, execute TCPGW and then the program.

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "custom.h"

#define SERV_ADDR  "168.126.185.132"
#define SERV_PORT  5050

int main(int argc, char *argv[])
{
    int   n, i, fd, ilen, olen;
    msg_header_t *header;
    msg_body_t   *body, *result;

    /* (1) Connects to TCPGW. */
    if ( (fd = network_connect()) < 0 )  {
       perror("network connect error:");
       return -1;
    }

    /* (2) Configure the message. */
    /* Message Header                              */
    header = (msg_header_t *)malloc(sizeof(msg_header_t));
    memset(header, 0x00, sizeof(msg_header_t));
    header->len = sizeof(msg_body_t);

    /* Message Body                                */
    body = (msg_body_t *)malloc(sizeof(msg_body_t));
    memset(body, 0x00, sizeof(msg_body_t));
    strncpy(body->name,"TOUPPER", 16);

    for (i = 0; i < 1; i++) {
        /* (3) Send data to TCPGW. */
        if ( (n = send(fd, (char *)header, sizeof(msg_header_t), 0)) < 0 )  {
           perror("network write error:");
           close(fd);
           return -1;
        }
        printf("Msg header send !\n");

        /* Message Body                                */
        strncpy(body->data, argv[1], 100);
 
        if ( (n = send(fd, (char *)body, sizeof(msg_body_t), 0)) <= 0 )  {
           perror("network write error:");
           close(fd);
           return -1;
        }

        /* (4) TCPGW receives data. */
        if ( (n = recv(fd, (char *)&olen, 4, 0)) < 0 )  {
           perror("network read error:");
           close(fd);
           return -1;
        }        result = (msg_body_t *)malloc(olen);
        memset((char *)result, 0x00, olen);
        if ( (n = recv(fd, (char *)result, olen, 0)) <= 0 )  {
           perror("network read error:");
           close(fd);
           return -1;
        }    }
    /* (5) Disconnect from TCPGW. */
    close(fd);
    return 1;
}

int network_connect()
{
    struct sockaddr_in serv_addr;
    int fd;

    memset((char *)&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family      = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port        = htons((unsigned short)SERV_PORT);

    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        return -1;
    }
    if (connect(fd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) >= 0)
        return fd;

    close(fd);
    return -1;
}

4.1.4. Server

<svr.c>

#include <stdio.h>
#include <string.h>
#include <usrinc/atmi.h>

TOUPPER(TPSVCINFO *msg)
{
    int             i;

    printf("TOUPPER service is started!\n");
    printf("INPUT : len=%d, data='%s'\n", msg->len, msg->data);

    for (i = 0; i < msg->len; i++)
        msg->data[i] = toupper(msg->data[i]);

    printf("OUTPUT: len=%d, data='%s'\n", strlen(msg->data), msg->data);

    tpreturn(TPSUCCESS,0,(char *)msg->data, msg->len, 0);
}

4.2. Synchronous INBOUND TCPGW

When a Tmax client (or a Tmax service) initiates a tpcall to TCPGW, TCPGW sends data to the remote node, and returns the result to the client/service when the remote node sends a reply.

It is important to specify the UID when a service is requested from the Tmax client to the remote node. Save the UID value (info->uid) specified in the TCPGW library in the message using the put_msg_info function, or save the user generated UID in the message and then set the UID value to the uid attribute. The message containing the UID is sent to the remote node, and the remote node must return the UID without modifying it. When TCPGW receives the reply, it calls the get_msg_info function to get the UID value and returns the reply back the client/service that requested the service.

The following example shows the operation of the synchronous inbound TCPGW program running as a client. To configure TCPGW, set the [-r], [-p], and [-n] options in the CLOPT item of the Tmax environment file and modify custom.c according to the remote node environment.

[Figure 4.2] Operation of Synchronous INBOUND TCPGW

Operation of Synchronous INBOUND TCPGW


The program is composed of the following files.

TypeFile Name
Environment Filetcpgw.m
TCPGWcustom.h, custom.c
Remote noderserver.c, network.c
Clientcli_tcpgw.c

4.2.1. Environment File

<tcpgw.m>

*DOMAIN        
res          SHMKEY = 88000,
             MINCLH = 1,
             MAXCLH = 1,
             TPORTNO = 8888
        
*NODE        
node1           TMAXDIR = "/home/tmaxqas/tmax",
                APPDIR  = "/home/tmaxqas/tmax/appbin",
                PATHDIR = "/home/tmaxqas/tmax/path",
                TLOGDIR = "/home/tmaxqas/tmax/log/tlog",
                ULOGDIR = "/home/tmaxqas/tmax/log/ulog",
                SLOGDIR = "/home/tmaxqas/tmax/log/slog",
        
*SVRGROUP        
svg1         NODENAME = node1
        
*SERVER        
tcpgw        SVGNAME = svg1, 
             MIN = 1, MAX = 1,
             CPC = 5, 
             SVRTYPE = CUSTOM_GATEWAY,
             CLOPT = "-- -r 168.126.185.131 -p 5060 -n 2"
        
*SERVICE        
TCPGW        SVRNAME = tcpgw

4.2.2. TCPGW

<custom.h>

#ifndef _CUSTOM_H_
#define _CUSTOM_H_

/* -------------------------------------------------------------------- */
/*              Fixed structures and macros       */

#define MSG_MAGIC               "Tmax"
#define REMOTE_REQUEST          0
#define REMOTE_REPLY             1
#define SVC_NAME_LENGTH         20

/* msg_info_t struct cannot be redefined. */
typedef struct msg_info {
    char  svc[SVC_NAME_LENGTH];     
    int   err;          
    int   len;          
    int   uid;          
    int   flags;        
    int   msgtype;     
    int   channel_id;   
} msg_info_t;

/* -------------------------------------------------------------------- */
/*        Modifiable structures and macros         */
/* msg_header_t and msg_body_t structs can be redefined. */

#define UID_FIELD 98
#define SVC_NAME_FIELD 92
#define UID_LENGTH 4
#define SVC_LENGTH 6

typedef struct msg_header {
    int         len;           
} msg_header_t;

typedef struct msg_body {
    char        data[92];
    char        name[6];
    char        uid[4];
} msg_body_t;

#endif  /* _CUSTOM_H_ */

<custom.c>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include "custom.h"

/* Since msg_header_size and comm_header_size are used in TCPGW library,
   they must be defined. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* The internal logics do not have to implemented when the following
   functions are not required. */
/* However, they must be defined since they are used in the TCPGW library. */
int get_channel_num(char *data)
{
    return -1;
}

int put_msg_complete(char *hp, char *data, msg_info_t *info)
{
    return 1;
}

int get_service_name(char *header, int err, char *svc)
{
    return -1;
}

int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int addr, int type, int fd)
{
    return 1;
}

int get_msg_length(msg_header_t *hp)
{
    if (hp == NULL || hp->len == 0){
        return -1;
    }
    return ntohl(hp->len);
}

int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
        info->flags = 0;

if ((info == NULL) || (hp == NULL) || (data == NULL))
            return -1;

        info->len = ntohl(hp->len);
        info->err = 0;
        info->flags = 0;
        /* Configure info->uid. */
        memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH);

        strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH); 
        info->svc[SVC_LENGTH] = 0;

        if (info->uid == 0) {
            return REMOTE_REQUEST;
        }
        else { 
            return REMOTE_REPLY;
        }
}

int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
        int  nSize = 0;

        if ((info == NULL) || (hp == NULL) || (data == NULL))
            return -1;

        /* The value entered in hp->len is the length of the actual data */
        hp->len = htonl(info->len);

        if (info->err) {  
            printf("info->err = %d\n", info->err);
            return -1;
        }
        else
           /* When making a request to the remote node, this uid  */
           /* must be set to the value configured in the library.*/
            memcpy((char *)&data[UID_FIELD], (char *)&(info->uid),
            UID_LENGTH);

        memcpy((char *)&data[SVC_NAME_FIELD], info->svc, SVC_LENGTH);

        return (info->len + msg_header_size);
}

int remote_closed(int index, int type)
{
        return 1;
}

int prepare_shutdown(int code)
{
    return 1;
}

int set_service_timeout(int uid, char *header)
{
return 1;
}

int chk_end_msg(int len, char *data)
{
        return len;
}

int outmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

<Makefile>

#This makefile is for IBM 32bit.
#TARGET must be the same as the SERVER anme defined in the Tmax environment file.
TARGET  = tcpgw
APOBJS  = $(TARGET).o

#To generate TCPGW, libtcpgw.a or libtcpgw.s must be linked. 
LIBS    = -ltcpgw -ltmaxgw
OBJS    = custom.o register.o

CFLAGS  = -q32 -O -I$(TMAXDIR) -D_DBG
LDFLAGS = -brtl

APPDIR  = $(TMAXDIR)/appbin
LIBDIR  = $(TMAXDIR)/lib

.SUFFIXES : .c

.c.o:
        $(CC) $(CFLAGS) $(LDFLAGS) -c $<

$(TARGET): $(OBJS)
        $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS)
        mv $(TARGET) $(APPDIR)/.
        rm -f $(OBJS)

$(APOBJS): $(TARGET).c
        $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c

4.2.3. Remote Node

<rserver.c>

The following code is an example of a daemon program that can connect to TCPGW and send/receive messages.

For testing, TCPGW must be started while the program is running.

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include "custom.h"

#define SERV_PORT 5060

void doit(int fd);

int main(int argc, char *argv[])
{
    int ret, i, len;
    int fd, childfd;
    pid_t childpid;

    ret = network_listen(SERV_PORT);
    if (ret < 0){
        return -1;
    }else fd = ret;

    for (;;){
        childfd = network_accept(fd);
        if (childfd < 0){
            return -1;
        }
        if ( (childpid = fork()) == 0){
            close(fd);
            doit(childfd);
            exit(0);
        }
        close(childfd);
    }
}

void doit(int fd)
{
    ssize_t n;
    int ret, i;
    msg_header_t *header;
    msg_body_t *body;

    header = (msg_header_t *)malloc(sizeof(msg_header_t));
    memset((char *)header, 0x00, sizeof(msg_header_t));
    body = (msg_body_t *)malloc(sizeof(msg_body_t));
    memset((char *)body, 0x00, sizeof(msg_body_t));

    readn2(fd, (char *)header, sizeof(msg_header_t));

    readn2(fd, (char *)body, ntohl(header->len));

    if (body->uid == 0){
        printf("It's reply message\n");
    }else printf("It's request message\n");

    for (i = 0; i < sizeof(body->data); i++)
        body->data[i] = toupper(body->data[i]);

    header->len = htonl(sizeof(msg_body_t));

    writen2(fd, (char *)header, sizeof(msg_header_t));

    writen2(fd, (char *)body, sizeof(msg_body_t));
}

<network.c>

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff  /* should be in <netinet/in.h> */
#endif

/* ----------------------- extern global variables ------------------- */
extern int errno;
extern char _svrname[30];



int network_listen(int portno)
{
        int   fd, on;
        struct sockaddr_in serv_addr;

        if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){

           return(-1);
        }



        on = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on,
                       sizeof(on)) < 0) {
           printf("setsockopt error for SO_REUSEADDR");
        }

#ifdef SO_KEEPALIVE
        on = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
                       sizeof(on)) < 0) {
           printf("setsockopt error for SO_KEEPALIVE");
        }
#endif

        memset((char *) &serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family      = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port        = htons(portno);

        if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {


           close(fd);
           return(-2);
        }
      
    if (listen(fd, 250) < 0) {           close(fd);
           return(-3);
        }

        return(fd);
}


/* ------------------- server : client accept --------------------- */
int network_accept(int listenfd)
{
        int   fd, len;
        struct sockaddr_in cli_addr;

        len = sizeof(cli_addr);
        fd = accept(listenfd, (struct sockaddr *)&cli_addr,  &len);
        if (fd < 0){

           return(-1); /* often errno=EINTR, if signal caught */
        }

        return(fd);
}

int readn2(int fd, char *ptr, int nbytes)
{
        int   nleft, nread;

        nleft = nbytes;
        while (nleft > 0) {
            nread = recv(fd, ptr, nleft, 0);
            if (nread < 0) {
               if (errno == EINTR)
                  continue;
               else if (errno == EWOULDBLOCK)
                  return (nbytes - nleft);
               return(nread);       /* error, return < 0 */
            } else if (nread == 0)
               break;           /* EOF */
            nleft -= nread;
            ptr   += nread;
        }

        return (nbytes - nleft);        /* return >= 0 */
}


int writen2(int fd, char *ptr, int nbytes)
{
        int   nleft, nwritten;

        nleft = nbytes;
        while (nleft > 0) {
            nwritten = send(fd, ptr, nleft, 0);
            if (nwritten <= 0) {
               return(nwritten);        /* error */
            }

            nleft -= nwritten;
            ptr   += nwritten;
        }

        return(nbytes - nleft);
}

<Makefile>(Server)

#This makefile is for compac.
TARGET  = rServer
APOBJS  = $(TARGET).o

OBJS    = $(APOBJS) network.o

CFLAGS  = -D_DBG
.c.o:
        $(CC) $(CFLAGS) -c $<

all     : $(TARGET)
$(TARGET):$(OBJS)
        $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
        rm -f $(OBJS)

4.2.4. Client

<cli_tcpgw.c>

The following is an example of a Tmax client requesting services to TCPGW. Execute the program after connecting TCPGW to the remote node.

#include <stdio.h>
#include <usrinc/atmi.h>
#include "custom.h"

int main(int argc, char **argv)
{
    int ret;
    msg_body_t *body;
    char *buf;
    long rlen;

    ret = tmaxreadenv("tmax.env", "TMAX");
    if (ret < 0) {
        printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno));
    }

    ret = tpstart((TPSTART_T *)NULL);
    if (ret < 0) {
        printf("tpstart fail...[%s]\n", tpstrerror(tperrno));
        return -1;
    }
    buf = tpalloc("STRING", 0, 0);
    if (buf == NULL){
        printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno));
        tpend();
        return -1;
    }

    body = (msg_body_t *)buf;
    memcpy(body->data, argv[1], strlen(argv[1]));
    body->data[51] = 0;

    /* Call the TCPGW service. */
    ret = tpcall("TCPGW", buf, sizeof(msg_body_t), &buf, &rlen, 0);
    if (ret < 0){
        printf("tpcall fail...[%s]\n", tpstrerror(tperrno));
        tpfree((char *)buf);
        tpend();
        return -1;
    }

    body = (msg_body_t *)buf;
    printf("return value = %s\n", body->data);
    tpfree((char *)buf);
    tpend();
}

4.3. Non-blocking TCPGW

The non-blocking TCPGW method is often used when communicating with external institutions. This is because even with a small number of processes, it incurs less load on the system and can process more tasks. A blocking method calls TCPGW directly from a Tmax client or service and receives a reply, but a non-blocking method uses separate sending and receiving processes to send a request to and process the reply from TCPGW.

All processes that want to call TCPGW must first call the sending process, and then the sending process pre-processes the request before calling TCPGW via tpforward. Before processing the request, TCPGW unblocks the channel connected to the Tmax engine and passes the request to the remote node. When TCPGW receives a reply from the remote node, it finds and forwards the reply to the receiving service to process the reply in order. After the receiving process processes the reply, it sends the result to the initiating service requester.

Since the service that directly calls TCPGW or the service that receives tprelay runs asynchronously, it incurs almost no overhead. However, the process that initially calls the sending service is blocked until it receives a reply.

The following diagram shows how a connection made between the remote node (server) and TCPGW (client).

[Figure 4.3] Non-blocking TCPGW Method

Non-blocking TCPGW Method

The program is composed of the following files.

TypeFile Name
Configuration Filetcpgw.m
TCPGWcustom.c, custom.h
Remote NoderServer.c, network.c
Serversndsvr.c, rcvsvr.c
Clientcli_tcpgw.c

4.3.1. Environment File

<tcpgw.m>

*DOMAIN        
res            SHMKEY = 88000,
               MINCLH = 1,
               MAXCLH = 1,
               TPORTNO = 8888
        
*NODE        
node1          TMAXDIR="/home/tmax",
               APPDIR="/home/tmax/appbin"
        
*SVRGROUP        
svg1           NODENAME = node1
        
*SERVER        
tcpgw          SVGNAME = svg1, 
               MIN = 1, MAX = 1,
               CPC = 5, 
               SVRTYPE = CUSTOM_GATEWAY,
               CLOPT = "-- -r 168.126.185.131 -p 5060 -n 2 -H 9"
sndsvr         SVGNAME = svg1,
               MIN = 1, MAX = 1
rcvsvr         SVGNAME = svg1,
               MN = 1, MAX = 1
sndsvr         SVGNAME = svg1,
               MIN = 1, MAX = 1
rcvsvr         SVGNAME = svg1,
               MIN = 1, MAX = 1

*SERVICE        
TCPGW          SVRNAME = tcpgw
SENDSVC        SVRNAME = sndsvr
RECVSVC        SVRNAME = rcvsvr

4.3.2. TCPGW

<custom.c>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include "custom.h"

/* Since msg_header_size and comm_header_size are used in TCPGW library,
   they must be defined. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* The internal logics do not have to implemented when the following
   functions are not required. */
/* However, they must be defined since they are used in the TCPGW library. */
int get_channel_num(char *data)
{
    return -1;
}

int put_msg_complete(char *hp, char *data, msg_info_t *info)
{
    return 1;
}

/* Set the service name to tprelay to */
int get_service_name(char *header, int err, char *svc)
{
    /* strcpy the internal user header (retsvcname). */
    strcpy(svc, header);
    svc[8] = 0;
    return 1;
}

int init_remote_info(char *myname, int mynumber, int num_channel, int key)
{
    return 1;
}

int remote_connected(int index, int addr, int type, int fd)
{
    return 1;
}

int get_msg_length(msg_header_t *hp)
{
    if (hp == NULL || hp->len == 0){
        return -1;
    }
    return ntohl(hp->len);
}

/* get_msg_info and put_msg_info must be implemented same as the
   synchronous client TCPGW. */
int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
        info->flags = 0;

       if ((info == NULL) || (hp == NULL) || (data == NULL))
            return -1;

        info->len = ntohl(hp->len);
        info->err = 0;
        info->flags = 0;
        /* Configure info->uid. */
        memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH);

        strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH); 
        info->svc[8] = 0;

        if (info->uid == 0) {
            return REMOTE_REQUEST;
        }
        else {
            return REMOTE_REPLY;
        }
}

int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info)
{
        int  nSize = 0;

        if ((info == NULL) || (hp == NULL) || (data == NULL))
            return -1;

        /* The value in hp->len is the actual data length  */
        hp->len = htonl(info->len);

        if (info->err) {  
            printf("info->err = %d\n", info->err);
            return -1;
        }
        else
            /* When a request is made to the remote node, this uid must be configured */
            /* set to the value configured in the library.*/
            memcpy((char *)&data[UID_FIELD], (char *)&(info->uid), 4);

        memcpy((char *)&data[SVC_NAME_FIELD], info->svc, 6);

        return (info->len + 4);
}

int remote_closed(int index, int type)
{
     return 1;
}

int prepare_shutdown(int code)
{
    return 1;
}

int set_service_timeout(int uid, char *header)
{
     return 1;
}

int chk_end_msg(int len, char *data)
{
     return len;
}

int outmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

int inmsg_recovery(char *data, msg_info_t *info)
{
    return -1;
}

<Makefile>

Refer to the makefile example in "4.2. Synchronous INBOUND TCPGW".

4.3.3. Remote Node

<rServer.c>

The following example shows a daemon program that connects to TCPGW to sends/receives messages. For a test run, TCPGW must be started while this program is running.

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include "custom.h"

#define SERV_PORT 5060

void doit(int fd);

int main(int argc, char *argv[])
{
    int ret, i, len;
    int fd, childfd;
    pid_t childpid;

    ret = network_listen(SERV_PORT);
    if (ret < 0){
        return -1;
    }else fd = ret;

    for (;;){
        childfd = network_accept(fd);
        if (childfd < 0){
            return -1;
        }
        if ( (childpid = fork()) == 0){
            close(fd);
            doit(childfd);
            exit(0);
        }
        close(childfd);
    }
}

void doit(int fd)
{
    ssize_t n;
    int ret, i;
    msg_header_t *header;
    /* Receives all message parts except the user header */
    remote_body_t *body;
    int uid;

    header = (msg_header_t *)malloc(sizeof(msg_header_t));
    memset((char *)header, 0x00, sizeof(msg_header_t));
    body = (remote_body_t *)malloc(sizeof(remote_body_t));
    memset((char *)body, 0x00, sizeof(remote_body_t));

    readn2(fd, (char *)header, sizeof(msg_header_t));
    readn2(fd, (char *)body, ntohl(header->len));

    if (body->uid == 0){
        printf("It's reply message\n");
    }else printf("It's request message\n");

    for (i = 0; i < sizeof(body->data); i++)
        body->data[i] = toupper(body->data[i]);

    header->len = htonl(sizeof(remote_body_t));
    writen2(fd, (char *)header, sizeof(msg_header_t));
    writen2(fd, (char *)body, sizeof(remote_body_t));
}

<network.c>

Refer to the network.c example in "4.2. Synchronous INBOUND TCPGW".

4.3.4. Server

<sndsvr.c>

#include <string.h>
#include <stdio.h>
#include <usrinc/atmi.h>

SENDSVC(TPSVCINFO *msg)
{
    char *sndbuf;
    long len;
 
    printf("[%s] Service Started!\n", msg->name);

    len = (msg->len);
    printf("len is [%d]\n", len);

    if ((sndbuf = (char *)tpalloc("CARRAY", NULL, len)) == NULL) {
        printf("sndbuf alloc failed !\n");
        tpreturn(TPFAIL, -1, NULL, 0, 0);
    }

    memcpy(sndbuf, msg->data, msg->len);
    /* Call TCPGW service via tpforward*/
    tpforward("TCPGW", (char *)sndbuf, len, TPNOREPLY);
}

<rcvsvr.c>

#include <stdio.h>
#include <string.h>
#include <usrinc/atmi.h>

/* TCPGW dynamically calls tprelay to this service. */
RECVSVC(TPSVCINFO *msg)
{
    char *rcvbuf;
    long len;
    
    printf("[%s] Service Started!\n", msg->name);

    len = msg->len;
    rcvbuf = msg->data;

    if(tpurcode != 0) {
        printf("tpurcode is [%d] tperrmsg is [%s]\n", tpurcode, 
                tpstrerror(tpurcode));
        tpreturn( TPFAIL, -1,(char *)rcvbuf, len, 0 );
    }
    tpreturn( TPSUCCESS, 0,(char *)rcvbuf, len, TPNOFLAGS );
}

4.3.5. Client

<cli_tcpgw.c>

The following example program uses a Tmax client to call SENDSVC. Before calling SENDSVC, configure the service to which TCPGW will tprelay to in the message header.

#include <stdio.h>
#include <usrinc/atmi.h>
#include "../server/custom.h"

int main(int argc, char **argv)
{
    int ret;
    msg_body_t *body;
    char *buf;
    long rlen;

    ret = tmaxreadenv("tmax.env", "TMAX");
    if (ret < 0) {
        printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno));
    }

    ret = tpstart((TPSTART_T *)NULL);
    if (ret < 0) {
        printf("tpstart fail...[%s]\n", tpstrerror(tperrno));
        return 0;
    }
    buf = tpalloc("STRING", 0, 0);
    if (buf == NULL){
        printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno));
        tpend();
        return 0;
    }

    body = (msg_body_t *)buf;
    memcpy(body->data, argv[1], strlen(argv[1]));
    body->data[51] = 0;
    memcpy(body->retsvcname, "RECVSVC", 9);
    body->retsvcname[8] = 0;
    memcpy(buf, (char *)body, sizeof(msg_body_t));

    /* Call the service that will call TCPGW */
    ret = tpcall("SENDSVC", buf, sizeof(msg_body_t), &buf, &rlen, 0);
    if (ret < 0){
        printf("tpcall fail...[%s]\n", tpstrerror(tperrno));
        tpfree((char *)buf);
        tpend();
        return 0;
    }

    body = (msg_body_t *)buf;

    printf("return value = %s\n", body->data);
    tpfree((char *)buf);
    tpend();
}