제4장 예제

내용 목차

4.1. OUTBOUND TCPGW
4.1.1. 환경 파일
4.1.2. TCPGW
4.1.3. 리모트 노드
4.1.4. 서버
4.2. 동기 INBOUND TCPGW
4.2.1. 환경 파일
4.2.2. TCPGW
4.2.3. 리모트 노드
4.2.4. 클라이언트
4.3. NON 블로킹 TCPGW
4.3.1. 환경 파일
4.3.2. TCPGW
4.3.3. 리모트 노드
4.3.4. 서버
4.3.5. 클라이언트

본 장에서는 TCPGW의 서비스 유형별 예제에 대해 설명한다.

4.1. OUTBOUND TCPGW

TCPGW가 Tmax와 함께 boot되어 있다가 리모트 노드의 요청이 수신되면, 사용자가 지정한 서비스를 호출한 후에 다시 리모트 노드로 처리 결과를 보내는 예제이다. TCPGW의 클라이언트/서버 방식에 관계가 없고 리모트 노드의 상황에 맞게 custom.c를 수정하여 TCPGW를 구성한다.

다음은 OUTBOUND TCPGW 프로그램의 동작이다.

[그림 4.1] OUTBOUND TCPGW 프로그램 동작

OUTBOUND TCPGW 프로그램 동작


프로그램 구성은 다음과 같다.

구분파일명
환경 파일tcpgw.m
TCPGWcustom.h, custom.c
리모트 노드rclient.c, custom.h
서버svr.c

4.1.1. 환경 파일

<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는 개발자가 재정의하면 안되는 구조체이다. */
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와 msg_body_t는 개발자가 재정의 가능한 구조체이다. */
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"

/* msg_header_size와 comm_header_size는 TCPGW 라이브러리 내에서 
사용되기 때문에 정의해 주어야 한다. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */
/* 그러나 TCPGW 라이브러리 내에서 사용되기 때문에 정의는 해주어야 한다. */
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)
{
    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);

    /* 리모트 노드에서 요청이 들어오므로 REMOTE_REQUEST를 반환한다. */
    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;

    /* body->name을 이용하여 에러 유무 전송 */
    if (info->err)   /* error */
        strcpy(body->name, "Fail");
    else
        strcpy(body->name, "Success");

    /* 리모트 노드로 요청에 대한 결과를 전송하기 위한 데이터 길이를 반환한다. */
    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>

#이 Makefile은 ibm 32bit 용이다.
#TARGET은 Tmax 환경 파일에서 정의한 SERVER명과 같아야 한다.
TARGET  = tcpgw
APOBJS  = $(TARGET).o

#TCPGW를 생성하기 위해서는 libtcpgw.a혹은 libtcpgw.so를 링크시켜야 한다.
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. 리모트 노드

<rclient.c>

다음의 예제 프로그램은 테스트를 위한 리모트 노드의 클라이언트 프로그램이다. 테스트를 위해서는 TCPGW가 기동된 후 프로그램을 기동시켜야 한다.

#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;

    /* ① TCPGW로 연결을 맺는다. */
    if ( (fd = network_connect()) < 0 )  {
       perror("network connect error:");
       return -1;
    }

    /* ② 전문을 구성한다. */
    /* 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++) {
        /* ③ 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;
        }

        /* ④ TCPGW로 데이터를 수신한다. */
        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;
        }    }
    /* ⑤ 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. 서버

<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. 동기 INBOUND TCPGW

Tmax 클라이언트에서(또는 Tmax 서비스에서) TCPGW의 서비스를 tpcall하면 TCPGW는 리모트 노드로 데이터를 전송한다. 리모트 노드에서 처리 결과가 수신되면 해당 클라이언트에게 결과를 반환한다.

Tmax 클라이언트에서 리모트 노드로 서비스를 요청할 때 중요한 점은 UID를 설정하는 일이다. UID는 TCPGW 라이브러리 내부에서 지정하는 값(info->uid)을 put_msg_info에서 전문 내용에 저장하거나 또는 사용자가 UID를 생성해서 전문 내용에 저장한 후에 UID 값을 uid 항목에 넣어주면 된다. 이렇게 UID를 전문에 저장하고 리모트 노드로 요청을 보내면 리모트 노드에서는 해당 UID를 변경하지 말고 그대로 되돌려 주어야 한다. TCPGW는 응답을 수신한 후에 get_msg_info 함수를 호출하여 UID 값을 가져와 누가 TCPGW를 호출했는지를 판단하여 응답을 반환한다.

다음 예제는 동기 INBOUND TCPGW 프로그램의 동작이다. 클라이언트로 기동되는 것이므로 Tmax 환경 파일의 CLOPT에 [-r], [-p], [-n] 옵션을 설정하고 리모트 노드의 상황에 맞게 custom.c를 수정하여 TCPGW를 구성한다.

[그림 4.2] 동기 INBOUND TCPGW 프로그램 동작

동기 INBOUND TCPGW 프로그램 동작


프로그램 구성은 다음과 같다.

구분파일명
환경 파일tcpgw.m
TCPGWcustom.h, custom.c
리모트 노드rserver.c, network.c
클라이언트cli_tcpgw.c

4.2.1. 환경 파일

<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는 개발자가 재정의하면 안되는 구조체이다. */
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와 msg_body_t는 개발자가 재정의 가능한 구조체이다. */

#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"

/* msg_header_size와 comm_header_size는 TCPGW 라이브러리 내에서 사용되기 때문에
 정의해 주어야 한다. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */
/* 그러나 TCPGW 라이브러리 내에서 사용되기 때문에 정의해 주어야 한다. */
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;
        /* 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;

        /* hp->len 에 들어가는 값은 실제 데이터의 길이이다. */
        hp->len = htonl(info->len);

        if (info->err) {  
            printf("info->err = %d\n", info->err);
            return -1;
        }
        else
            /* 리모트 노드로 REQUEST를 하는 경우는 반드시 이 uid를 */
           /* 라이브러리 내부에서 설정한 값으로 설정해 주어야 한다.*/
            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>

#이 Makefile은 ibm 32bit 용이다.
#TARGET은 Tmax 환경 파일에서 정의한 SERVER명과 같아야 한다.
TARGET  = tcpgw
APOBJS  = $(TARGET).o

#TCPGW를 생성하기 위해서는 libtcpgw.a혹은 libtcpgw.so를 링크시켜야 한다.
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. 리모트 노드

<rserver.c>

다음의 예제 프로그램은 TCPGW의 접속을 받고, 전문을 송송신하는 기능의 데몬 형태의 프로그램이다. 테스트를 위해서는 프로그램이 먼저 기동되어 있는 상태에서 TCPGW가 기동되어야 한다.

#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 variable ------------------- */
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용)

#이 Makefile은 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. 클라이언트

<cli_tcpgw.c>

다음의 예제 프로그램은 TCPGW에 서비스를 요청하는 Tmax 클라이언트이다. TCPGW와 리모트 노드와의 연결이 완료된 후 프로그램을 실행한다.

#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;

    /* TCPGW 서비스를 호출한다. */
    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 블로킹 TCPGW

NON 블로킹 TCPGW 방식은 일반적으로 대외계와 통신하는 경우에 많이 사용한다. 적은 프로세스 수로도 시스템에 부하를 적게 주고, 보다 많을 일을 처리할 수 있기 때문이다. 블록형 방식은 Tmax 클라이언트나 서비스에서 직접 TCPGW를 호출하여 응답을 받았으나 이 방식은 송신 프로세스와 TCPGW로부터 응답을 처리하는 수신 프로세스를 분리하여 처리한다.

TCPGW를 호출하고자 하는 모든 프로세스는 먼저 송신 프로세스를 호출하면, 송신 프로세스는 TCPGW를 호출하기 전에 사전 작업을 완료하고 TCPGW로 서비스 제어권을 넘긴다(tpforward). 다음에 TCPGW는 넘겨받은 서비스를 처리하기 전에 Tmax 엔진과 연결되어 있는 채널의 블록 상태를 해제하고 리모트 노드로 서비스를 송신하다. 리모트 노드로부터 응답을 수신받으면 TCPGW는 수신 메시지를 처리할 수신 서비스를 순서에 따라 찾아서 수신 프로세스에게 서비스 제어권을 넘긴다. 수신 프로세스는 응답을 처리한 후에 최초로 서비스를 호출한 프로세스에게 결과를 전달한다.

위와 같은 원리로 서비스되므로 실제 TCPGW를 호출하는 서비스나 tprelay받는 서비스는 비동기적으로 작동하므로 수행시간에 대한 부하가 거의 없다. 단, 최초로 송신 서비스를 호출하는 프로세스는 응답이 올 때까지 기다리므로 블록되어 있는 상태이다.

다음의 그림은 리모트 노드가 서버가 되고 TCPGW가 클라이언트로서 연결을 맺는 구조이다.

[그림 4.3] NON 블로킹 TCPGW 방식

NON 블로킹 TCPGW 방식

프로그램 구성은 다음과 같다.

구분파일명
환경 파일tcpgw.m
TCPGWcustom.c, custom.h
리모트 노드rServer.c, network.c
서버sndsvr.c, rcvsvr.c
클라이언트cli_tcpgw.c

4.3.1. 환경 파일

<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"

/* msg_header_size와 comm_header_size는 TCPGW 라이브러리 내에서 사용되기 때문에
 정의해 주어야 한다. */
int     msg_header_size    = sizeof(msg_header_t);
int     comm_header_size = 0;

/* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */
/* 그러나 TCPGW 라이브러리 내에서 사용되기 때문에 정의해 주어야 한다. */
int get_channel_num(char *data)
{
    return -1;
}

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

/* 이 함수에서 tprelay할 함수명을 설정해 준다. */
int get_service_name(char *header, int err, char *svc)
{
    /*내부적으로 저장하고 있는 사용자 헤더는 retsvcname이므로 바로 strcpy한다.*/
    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와 put_msg_info는 동기 클라이언트 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;
        /* 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;

        /* hp->len에 들어가는 값은 실제 데이터의 길이이다. */
        hp->len = htonl(info->len);

        if (info->err) {  
            printf("info->err = %d\n", info->err);
            return -1;
        }
        else
            /* 리모트 노드로 REQUEST를 하는 경우는 반드시 이 uid를 */
            /* 라이브러리 내부에서 설정한 값으로 설정해 주어야 한다.*/
            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>

“4.2. 동기 INBOUND TCPGW”의 Makefile 예제와 동일하다.

4.3.3. 리모트 노드

<rServer.c>

다음 예제 프로그램은 TCPGW의 접속을 받고, 전문을 송수신하는 기능의 데몬 형태의 프로그램이다. 테스트를 위해서는 이 프로그램이 먼저 기동되어 있는 상태에서 TCPGW가 기동되어야 한다.

#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;
    /* 사용자 헤더를 제외한 나머지 부분만을 받는다. */
    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>

“4.2. 동기 INBOUND TCPGW”의 network.c의 예제와 동일하다.

4.3.4. 서버

<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);
    /* TCPGW의 서비스를 tpforward로 호출한다. */
    tpforward("TCPGW", (char *)sndbuf, len, TPNOREPLY);
}

<rcvsvr.c>

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

/* TCPGW에서 동적으로 이 서비스에 tprelay를 한다. */
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. 클라이언트

<cli_tcpgw.c>

다음 예제 프로그램은 Tmax 클라이언트로서 SENDSVC를 호출하도록 되어 있다. SENDSVC를 호출하기 전에 TCPGW에서 tprelay할 서비스를 전문 헤더에 설정한다.

#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));

    /* 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();
}