예제
본 장에서는 X25GW의 서비스 유형별 예제에 대해 설명한다.
1. OUTBOUND X25GW
X25GW가 Tmax boot될 때 기동되어 있다가 리모트 노드의 요청이 수신되면, 사용자가 지정한 서비스를 호출한 후에 다시 리모트 노드로 처리 결과를 주는 예제이다. 그리고 리모트 노드의 상황에 맞게 custom.c를 수정하여 X25GW를 구성한다.
다음은 OUTBOUND X25GW의 동작 구조를 나타내는 그림이다.
OUTBOUND X25GW 프로그램 구성은 다음과 같다.
구분 | 파일명 |
---|---|
환경 파일 |
x25gw.m, x25gw.cfg |
X25GW |
custom.c, custom.h |
서버 |
svr.c |
1.1. 환경 파일
<x25gw.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 x25gw SVGNAME=svg1, MIN=1, MAX=1, CPC=5, SVRTYPE=CUSTOM_GATEWAY, CLOPT=”-- -F /home/tmax/config/x25gw.cfg” svr SVGNAME=svg1, MIN=1, MAX=1 *SERVICE TOUPPER SVRNAME=svr
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path # 0 1 1 4 any no /dev/x25pkt
1.2. X25GW
<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 #define SYSID_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; char sys_id[SYSID_LENGTH]; } msg_info_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” /* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */ /* 그러나 X25GW 라이브러리 내에서 사용되기 때문에 정의는 해주어야 한다. */ int init_remote_info(char *myname, int mynumber, int num_channel, int key) { return 1; } int remote_connected(int index, int linkno, int lcnno, int type) { return 1; } int get_msg_info(char *data, msg_info_t *info) { msg_body_t *body; if ((info == NULL) || (data == NULL)) return -1; body = (msg_body_t *)data; 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(char *data, msg_info_t *info) { msg_body_t *body; if ((info == NULL) || (data == NULL)) return -1; body = (msg_body_t *)data; /* body->name을 이용하여 에러 유무 전송 */ if (info->err) /* error */ strcpy(body->name, “Fail”); else strcpy(body->name, “Success”); /* 리모트 노드로 요청에 대한 결과를 전송하기 위한 데이터 길이를 반환한다. */ return info->len; } int get_channel_num(char *data) { return -1; } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } 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 = x25gw APOBJS = $(TARGET).o #X25GW를 생성하기 위해서는 #libX25GW.a혹은 libX25GW.so를 링크시켜야 한다. LIBS = -lx25gw -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
1.3. 서버
<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); }
2. 동기형 INBOUND X25GW
Tmax 클라이언트에서(또는 Tmax 서비스에서) X25GW의 서비스인 “X25GW”를 tpcall하면 X25GW는 리모트 노드로 데이터를 전송한다. 리모트 노드에서 처리 결과가 수신되면 해당 클라이언트에게 결과를 반환한다.
Tmax 클라이언트에서 리모트 노드로 서비스를 요청할 때 중요한 점은 UID를 설정하는 일이다. UID는 X25GW 라이브러리 내부에서 지정하는 값(info→uid)을 put_msg_info에서 전문 내용에 저장 하거나 또는 사용자가 UID를 만들어서 전문 내용에 저장한 후에 UID 값을 uid 항목에 넣어주면 된다. 이렇게 UID를 전문에 저장하고 리모트 노드로 요청을 보내면 리모트 노드에서는 해당 UID를 변경하지 말고 그대로 되돌려 주어야 한다.
X25GW는 응답을 수신한 후에 get_msg_info 함수를 호출하여 UID값을 가져와 누가 X25GW를 호출했는지를 판단하여 응답을 반환한다.
다음은 동기형 INBOUND X25GW의 동작 구조를 나타내는 그림이다.
동기 X25GW 프로그램 구성은 다음과 같다.
구분 | 파일명 |
---|---|
환경 파일 |
x25gw.m, x25gw.cfg |
X25GW |
custom.c, custom.h |
클라이언트 |
cli_x25gw.c |
2.1. 환경 파일
<x25gw.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 x25gw SVGNAME=svg1, MIN=1, MAX=1, CPC=5, SVRTYPE=CUSTOM_GATEWAY, CLOPT=”-- -F /home/tmax/config/x25gw.cfg” *SERVICE X25GW SVRNAME=x25gw
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path # 0 1 1 4 any no /dev/x25pkt
2.2. X25GW
<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 #define SYSID_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; char sys_id[SYSID_LENGTH]; } 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_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” /* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */ /* 그러나 X25GW 라이브러리 내에서 사용되기 때문에 정의해 주어야 한다. */ 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_info(char *data, msg_info_t *info) { info->flags = 0; if ((info == NULL) || (data == NULL)) return -1; 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(char *data, msg_info_t *info) { int nSize = 0; if ((info == NULL) || (data == NULL)) return -1; 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; } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } 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 = x25gw APOBJS = $(TARGET).o #X25GW를 생성하기 위해서는 libX25GW.a혹은 libX25GW.so를 링크시켜야 한다. LIBS = -lx25gw -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
2.3. 클라이언트
<cli_x25gw.c>
/* 이 프로그램은 X25GW에 서비스를 요청하는 Tmax 클라이언트이다. X25GW와 리모트 노드와의 연결이 완료된 후 이 프로그램을 실행한다. */ #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; /* X25GW 서비스를 호출한다. */ ret = tpcall("X25GW", 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(); }
3. 서비스 NON 블록형 X25GW
NON 블록형 X25GW 방식은 보통 대외계와 통신하는 경우에 많이 사용한다. 이 방식을 이용하면 적은 프로세스 수로도 시스템에 부하를 적게 주면서 보다 많을 일을 처리할 수 있기 때문이다.
블록형 방식은 Tmax 클라이언트나 서비스에서 직접 X25GW를 호출하여 응답을 받았으나 이 방식은 송신 프로세스와 X25GW로부터 응답을 처리하는 수신 프로세스를 분리하여 처리한다. X25GW를 호출하고자 하는 모든 프로세스는 먼저 송신 프로세스를 호출하면, 송신 프로세스는 X25GW를 호출하기 전에 사전 작업을 완료하고 X25GW로 서비스 컨트롤을 넘긴다.(tpforward)
다음에 X25GW는 넘겨받은 서비스 처리를 하기전에 Tmax 엔진과 연결되어 있는 채널의 블록상태를 해제하고 리모트 노드로 서비스를 송신한다. 리모트 노드로부터 응답을 수신받으면 X25GW는 수신 메시지를 처리할 수신 서비스를 찾는 순서에 따라서 찾아 수신 프로세스에게 서비스 컨트롤을 넘긴다. 수신 프로세스는 응답을 처리한 후에 최초로 서비스를 호출한 프로세스에게 결과를 전달한다. 실제 X25GW를 호출하는 서비스나 tprelay받는 서비스는 비동기적으로 작동하므로 수행시간에 대한 부하가 없다. 최초로 송신 서비스를 호출하는 프로세스는 응답이 올 때까지 기다리므로 블록되어 있는 상태이다.
예제는 리모트 노드가 서버가 되고 X25GW가 클라이언트로서 연결을 맺는 구조이다.
NON 블로킹 X25GW 프로그램 구성은 다음과 같다.
구분 | 파일명 |
---|---|
환경 파일 |
x25gw.m, x25gw.cfg |
X25GW |
custom.c, custom.h |
서버 |
sndsvr.c, rcvsvr.c |
클라이언트 |
cli_x25gw.c, custom.h |
3.1. 환경 파일
<x25gw.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 x25gw SVGNAME=svg1, MIN=1, MAX=1, CPC=5, SVRTYPE=CUSTOM_GATEWAY, CLOPT=”-- -F /home/tmax/config/x25gw.cfg –S RECVSVC –H 9” sndsvr SVGNAME=svg1, MIN=1, MAX=1 rcvsvr SVGNAME=svg1, MIN=1, MAX=1 *SERVICE X25GW SVRNAME=x25gw SENDSVC SVRNAME=sndsvr RECVSVC SVRNAME=rcvsvr
<x25gw.cfg>
# gwno | link_no | start_LCN_no | num_LCN | dir | reply_dedicated| dev_path # 0 1 1 4 any no /dev/x25pkt
3.2. X25GW
<custom.h>
/* ---------------------- custom.h -------------------------- */ #ifndef _CUSTOM_H_ #define _CUSTOM_H_ /* ---------------------------------------------------------- */ /* Fixed structures and macros */ /* Common of Agent Define */ #define MSG_MAGIC “Tmax” #define REMOTE_REQUEST 0 #define REMOTE_REPLY 1 #define SVC_NAME_LENGTH 20 #define SYSID_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; char sys_id[SYSID_LENGTH]; } msg_info_t; /* ---------------------------------------------------------- */ /* Modifiable structures and macros */ #define UID_FIELD 58 #define SVC_NAME_FIELD 52 #define UID_LENGTH 4 #define SVC_LENGTH 6 #define MSG_KEEP_SVC_SIZE 9 typedef struct msg_body { char retsvcname[9]; /* -H옵션 만큼의 내부적으로 저장할 서비스 이름 */ char data[52]; char name[6]; char uid[4]; } msg_body_t; /* 리모트 노드와 통신할 때는 body내부의 헤더인 retsvcname을 제외한 구조체 */ typedef struct remote_body{ char data[52]; char name[6]; char uid[4]; }remote_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” /* 아래의 함수들은 필요하지 않은 경우 내부 로직은 구현하지 않아도 된다. */ /* 그러나 X25GW 라이브러리 내에서 사용되기 때문에 정의해 주어야 한다. */ int get_channel_num(char *data) { 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 linkno, int lcnno, int type) { return 1; } /* get_msg_info와 put_msg_info는 동기 클라이언트 X25GW와 동일하게 구현한다. */ int get_msg_info(char *data, msg_info_t *info) { info->flags = 0; if ((info == NULL) || (data == NULL)) return -1; 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(char *data, msg_info_t *info) { int nSize = 0; if ((info == NULL) || (data == NULL)) return -1; 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; } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } int outmsg_recovery(char *data, msg_info_t *info) { return -1; } int inmsg_recovery(char *data, msg_info_t *info) { return -1; }
<Makefile>
동기형 INBOUND X25GW의 Makefile 예제와 동일하다.
3.3. 서버
<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); /* X25GW의 서비스를 tpforward로 호출한다. */ tpforward("X25GW", (char *)sndbuf, len, TPNOREPLY); }
<rcvsvr.c>
#include <stdio.h> #include <string.h> #include <usrinc/atmi.h> /* X25GW에서 동적으로 이 서비스에 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 ); }
3.4. 클라이언트
<cli_x25gw.c>
/* 이 예제 프로그램은 Tmax 클라이언트로서 SENDSVC를 호출하도록 되어있다. SENDSVC를 호출하기 전에 X25GW에서 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)); /* X25GW를 호출할 서비스를 호출한다. */ 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(); }