Tmax Programming Guide ( UCS ) Copyright 2000 TmaxSoft Co., Ltd. All Rights Reserved
Copyright Notice Copyright 2000 TmaxSoft Co., Ltd. All Rights Reserved. TmaxSoft Co., Ltd. 대한민국서울시강남구대치동 946-1 글라스타워 18 층우 )135-708 Restricted Rights Legend This software and documents are made available only under the terms of the TmaxSoft License Agreement and may be used or copied only in accordance with the terms of this agreement. No part of this document may be reproduced, transmitted, or translated in any form or by any means, electronic, mechanical, manual, or optical, without the prior written permission of TmaxSoft Co., Ltd. 소프트웨어및문서는오직 TmaxSoft Co., Ltd. 와의사용권계약하에서만이용이가능하며, 사용권계약에따라서사용하거나복사할수있습니다. 또한이매뉴얼에서언급하지않은정보에대해서는보증및책임을지지않습니다. 이매뉴얼에대한권리는저작권에보호되므로발행자의허가없이전체또는일부를어떤형식이나, 사진녹화, 기록, 정보저장및검색시스템과같은그래픽이나전자적, 기계적수단으로복제하거나사용할수없습니다. Trademarks Tmax, WebtoB, WebT, and JEUS are registered trademarks of TmaxSoft Co., Ltd. All other product names may be trademarks of the respective companies with which they are associated. Tmax, WebtoB, WebT, JEUS 는 TmaxSoft Co., Ltd. 의등록상표입니다. 기타모든제품들과회사이름은각각해당소유주의상표로서참조용으로만사용됩니다. Document Edition Date Version TMUCS-0107-04-400 Jan 7, 2006 Tmax 4.0 Tmax 제품은아래표와같이 Tmax Base, Tmax Standard, Options 으로나누어집니다. 본서는 Tmax Standard 와옵션중에굵게기울임꼴로인쇄되어있는내용을포함하고있습니다. Tmax Standard Tmax Options TP Function + 2 Phase Commit Web Admin Console, X.25 Gateway, TCP/IP Gateway, Host- Link, Power Builder Interface Module, SERIAL Gateway, TCP/IP Service Gateway 1
이책에관하여 Tmax UCS Manual 은클라이언트의요청에따라서비스를수행하고다음수행을기다리는현재의일반적인서버 / 클라이언트모델의업무프로세스와달리서버측의능동적인서비스제공을가능하게하는 UCS(User Control Server) 및이를특수한용도에맞게개선한 RDP(Realtime Data Procesor) 라는새로운프로그래밍기법과 API 에대한설명을제공한다. UCS/RDP 는두개의채널을이용하여일반적인요청 / 응답형의서비스뿐만 (TCS 프로세스 ) 아니라이와독립적으로진행되는루틴을설정하여지금까지힘들었던계속적인서비스제공이나외부서비스와의연계를쉽게구현할수있다. 사용자는 UCS/RDP 와 TCS 프로세스를적절히사용함으로써개발의용이함과더불어 Tmax system 이제공하는많은장점들을여러가지용도로활용할수있게될것이다. 누구를위한책인가? 이책은다수의클라이언트가동시에같은 Server 에접속하고같은서비스를요청하는업무에서, 해당서비스에대한 Database 나기타다른 System 의리소스들을과다하게이용하여발생할수있는문제점을해결할수있는대안을제시한지침서임과동시에클라이언트와서비스프로그램개발을위한정보와 Sample 을제공한다. 어떻게사용하는가? 1. 소개 : Tmax 의강력한기능중에하나인 UCS/RDP 프로세스에대한특징과필요성, 구성그리고사용방법, 사례와사용예제에대해소개한다. 2. 사용방법 : UCS/RDP 프로세스를사용하기위한환경설정과컴파일하는방법, 사용되는함수들에대해설명한다. 3. 사례 : UCS 프로세스를사용하고있는실제사례를들어업무의향상에 UCS 프로세스가어떻게기여하고있고사용되는지설명한다. 4. Sample: UCS 프로세스로구현된 Client 프로그램과 Server 프로그램의 Sample 을제시하여실제사용자의이해를돕고자한다. 관련서 Tmax Getting Started Guide Tmax Programming Guide ( C ) 2
Tmax Reference Manual 3
규약 규 약 설 명 [ ] ( ) Numeric String Literal 필수항목옵션항목지정된여러개의값중배타적인선택구분자디폴트값숫자 abc 형태의문자열 abc 형태의문자열 4
차례 1 UCS 및 RDP 개요... 7 1.1 Process Control... 7 1.1.1 TCS (Tmax Control Server)... 8 1.1.2 POD(Process On Demand)... 8 1.1.3 UCS (User Control Server)... 8 1.2 UCS 의필요성... 9 1.3 RDP(Real Data Processor) 개요... 10 2 UCS 의특징... 12 2.1 UCS 구성... 15 2.1.1 UCS Server Program 구성... 15 2.1.2 UCS Client Program 구성... 41 2.1.3 RDP Server Program 구성... 45 3 UCS 의환경... 47 3.1 UCS 환경파일설정... 47 3.2 RDP 환경파일설정... 48 3.3 UCS 컴파일방법... 49 3.4 RDP 컴파일방법... 50 4 UCS 사용... 52 4.1 비정상종료프로그램 (core) 발생시관리하는프로그램... 52 4.2 socket을이용한비동기통신... 56 4.3 RQ를사용한프로그램... 63 5 부록... 66 5.1 UCS 사례... 66 5.2 UCS 예제프로그램... 67 5.2.1 간단한 UCS 예제프로그램... 67 5.2.2 서버대서버 UCS 프로그램... 74 5.3 RDP 예제프로그램... 85 5.3.1 간단한 RDP 예제프로그램... 85 5.4 UCS 함수모음... 95 5.4.1 tpclrfd... 95 5
5.4.2 tpissetfd... 97 5.4.3 tpgetunsol... 99 5.4.4 tppost... 102 5.4.5 tpregcb... 104 5.4.6 tprelay... 105 5.4.7 tpsavectx... 107 5.4.8 tpschedule... 109 5.4.9 tpsendtocli...111 5.4.10 tpsetfd... 114 5.4.11 tpsetunsol... 116 5.4.12 tpsetunsol_flag... 118 5.4.13 tpsubscribe... 119 5.4.14 tpsvctimeout... 122 5.4.15 tpsvrdown... 123 5.4.16 tpunregcb... 124 5.4.17 tpunsubscribe... 125 5.4.18 tpuschedule... 127 6
1 UCS (User Control Server) 및 RDP(Real Data Processor) 개요 UCS (User Control Server) 란, 프로그램의처리흐름을미들웨어가제어하지않고, User 가직접제어할수있는 Tmax 고유의프로그램형태이다. 이를이용한프로그램과프로세스를 UCS 프로그램, UCS 프로세스라고한다. 이런형태의프로그램방식을제공함으로써클라이언트의요청 ( 이벤트 ) 에의해서만작업을수행하는기존의방식뿐아니라, 서버프로세스 (UCS 프로세스 ) 가능동적으로이벤트를발생시켜작업을수행할수있게된다. 현재이를이용해, 클라이언트요청이없어도정보전달을요구하는업무 ( 증권시세정보, notify 등 ), 작업스케쥴링업무, 대외기관과의연동업무등에사용되어지고있다. 1.1 Process Control Tmax 는 TCS(Tmax Control Server) Type 과 UCS(User Control Server) Type 이렇게두가지의 Process Control 을제공한다. Tmax 는 TCS(Tmax Contorl Server), POD(Process On Demand) 및 UCS(User Control Server) 등세가지 Type 의 Process Control 을제공한다. TCS 는다른 Middleware(Tuxedo, Entera, TopEnd 등 ) 에서사용되는것과같이클라이언트의 request 가있을경우에만서비스를제공하는전형적인 Process Control 방법이고, POD 는서버프로세스가기동되어있지않다가클라이언트의 request 가있을때기동되어서비스를수행하는 Process Control 방법이다. UCS 는 TCS, POD 의수동적인 Process Control 방법과비교되는능동적인 Process Control 방법이다. 클라이언트의요구가있을경우에만서비스를제공하는 TCS, POD Process 와는다르게 UCS Process 는클라이언트로부터의 request 가없더라도, 클라이언트에게데이터를전송할수있다. 이 UCS 는 TP Monitor 중 Tmax 에만존재하는고유의기능이다. 7
1.1.1 TCS (Tmax Control Server) TCS Process 는일반적인 3-Tier 개념을가지고있는 Server Process 이다. Tmax System 은클라이언트의 request 를적절한 Server Process 의 Service 에게전달하게되고, Server Process 의 Service 는비즈니스로직에맞는연산작업후결과값을생성해서클라이언트에게리턴한다. TCS Type 의 Server Program 의흐름은다음과같다. 1.1.2 POD(Process On Demand) 그림 1 TCS Type Server Program POD Process 는 TCS 와비슷한형태의 Process 이다. 다른점은 Tmax 부트시에프로세스가기동되어있는상태가아니다가클라이언트에서요청이오면 Tmax 에서해당프로세스를기동시켜요청을처리한후에다시프로세스를종료시키는형태의프로세스라는점이다. 1.1.3 UCS (User Control Server) 일반적으로 TCS Process 는, 클라이언트의 request 에대해서만작업을수행하는수동적인 Process 이다. 그에비해서 UCS Process 는클라이언트의 request 가없더라도, 자신이별도의작업을수행할수있으며, 또한클라이언트의 request 발생시에도이를처리할수있다. 8
UCS Type 의 Server Program 의흐름은다음과같다. 그림 2 UCS Type Server Program 1.2 UCS 의필요성 UCS 는말그대로 User 가 Control 할수있는 Server 이다. 이를이용해, 기존데몬형태로기동되어, 수행했던많은작업들 ( 특정시간시작업수행, 다른대외기관과의연동작업, 클라이언트에게로의정보전달등 ) 을 UCS 형태로작성할수있다. 이렇게작성된프로그램의프로세스들은모두 Tmax 관할이되어별도의프로세스관리 ( 비정상종료시재기동등 ) 가필요없게된다. 다음은 UCS 가사용되는경우이다. 9
정보변경시, 이를접속된클라이언트 ( 특정클라이언트나모든클라이언트 ) 에게알려주는형태의업무. 1. 업무사례 1) 증권업무의경우시시각각변하는시세정보를클라이언트의요청이없이도, 이를전달하게함으로써, 클라이언트는보다빠르고, 정확한데이터를받아볼수있게된다. 2. 업무사례 2) software 장애나작업일정에대한통보, 기타공지사항등을관리자나개발자또는 end user 에게통지하는형태의업무 3. 업무사례 3) 은행업무의경우환거래발생시직접해당 클라이언트에게 통보해주거나, 자동화기기 상태를 특정 클라이언트 ( 자동화기기 관리자 ) 에게 알려주고자 하는 경우 사용할수있다. 작업스케줄링형태의업무 1. 업무사례 1) 특정시간에 batch 작업수행하고자하는경우나, 기타스케줄링을요구하는업무에사용할수있다. 대외연동업무 1. 업무사례 1) 대외기관과의연동업무구현시보다효율적이고, 간단한프로그램구조를제공 ( 비동기형태의거래흐름, 데이터의도착유무를 check 하기위한 looping 을없게함으로써 CPU 의자원을절약, 프로그램작성이간편하다. 기타많은형태의업무를 UCS Type 으로구현할수있다. 개발자들은 UCS 프로그램의처리흐름을정확히이해하고사용한다면, 업무구현시유용할것이다. 1.3 RDP(Real Data Processor) 개요 RDP 는 UCS 와같은형식의서버로서동작한다. RDP 는지속적으로변하는데이터를클라이언트에게효율적이고빠르게전달하기위해 10
UCS 타입의프로세스를커널수준에서개량한프로세스이다. 이 RDP 는 CLH 를거치지않고클라이언트로데이터를전달하기때문에소량의데이터를다수의클라이언트에게짧은시간간격으로보낼필요가있을때프로세스점유율이나처리속도의측면에서 UCS 보다월등한성능을보인다. 그림 3 RDP 의작동방식 RDP 서버는한노드에유일해야하며해당노드에존재하는모든서비스는 RDP 서버로서비스의결과데이터를전송후 RDP 서버에서데이터를클라이언트로보내게된다. 이를위해서다른서버프로세스가처리한결과를얻어오기위한채널의수 (RSCPC) 를설정해주어야하며 CLH 와 RDP 서버프로세스의수는일정하게유지되어야한다 ( 즉 MIN 값과 MAX 값이같아야한다 ). 단 RDP 서버프로세스의 MIN, MAX 는 CLH 보다항상많아야하며통상 2 배수로설정한다. 또한 CLH 는 MINCLH, MAXCLH 의수를같게설정해주어야한다. 프로그램구성및 API 는 UCS 와완전히동일하며환경파일과 compile 방법에서약간차이가있다. 11
2 UCS 의특징 UCS Program 은 usermain 에대한구현부가있어야하고, 컴파일시 UCS Library (libsvrucs.a / libsvrucs.so) 와링크되어야한다. 기본적으로 usermain() 은무한 loop 형태로구현된다. 즉, usermain() 함수가종료 ( 함수리턴 ) 되면, 해당프로세스는 tpsvrdone() 을수행하고종료해버리기때문이다. 즉, usermain() 이란함수를 main() 함수처럼생각하면된다.(main() 이종료되면해당프로세스가종료되는것과같다 ) UCS Program 작성시주의해야할사항은 usermain() 의무한 loop 내에 tpschedule() 이란함수가항상존재해야한다는것이다. 그이유는 tpschedule() 함수를통해클라이언트로부터전송된데이터나 tmax kernel 로부터전송된종료명령을받기때문이다. 다음은 usermain() 함수의간단한예이다. +1 #include <stdio.h> +2 #include <usrinc/atmi.h> +3 #include <usrinc/ucs.h> +4 +5 #define MAX_CLI 10 +6 int num_cli; +7 int client_id[max_cli]; +8 int count; +9 +10 int +11 tpsvrinit(int argc, char *argv[]) +12 +13 num_cli = 0; +14 count = 0; +15 +16 12
+16 int +17 usermain(int argc, char *argv[]) /* tmax 의 ucs 모드에서 main 과 같은부분 */ +18 +19 int jobs; +20 int i; +21 int ret; +22 char *sndbuf; +23 static int count = 0; +24 +25 printf("usermain start\n"); +26 +27 sndbuf = (char *)tpalloc("carray", NULL, 1024); +28 +29 while(1) +30 for (i = 0; i < num_cli; i++) +31 sprintf(sndbuf, "Success tpsendtocli [%d] 012345678901234\n", count); +32 tpsendtocli (client_id[i], sndbuf, 1024, 0); +33 +34 count++; +35 sleep(1); +36 jobs = tpschedule(-1); +37 +38 +39 +40 LOGIN(TPSVCINFO *msg) /* tmax 의 SERVICE 부분 */ +41 +42 char *sndbuf; +43 int clid; +44 int ret; +45 int i; +46 +47 printf("msg->data = [%.*s]\n", msg->len, msg->data); +48 fflush(stdout); +49 +50 sndbuf = (char *)tpalloc("carray", NULL, 1024); 13
+51 +52 printf("success transaction"); +53 sprintf(sndbuf, "Success transaction"); +54 +55 if (num_cli < MAX_CLI) +56 client_id[num_cli] = tpgetclid(); +57 printf("client id(clid) = %d\n", client_id[num_cli]); +58 num_cli++; +59 +60 +61 tpreturn(tpsuccess, 0, (char *)sndbuf, 1024, 0); +62 해당프로그램은특정정보를접속된클라이언트에게비요청메시지형태로전송하는예제이다. 간략하게프로그램을설명하면다음과같다. 1. 최초기동시 tpsvrinit() 을수행하고 usermain() 함수로들어온다. 2. 라인 30, num_cli 값이 0 이므로, for 문을빠져나온다. 3. 라인 36, tpschedule() 을통해클라이언트로부터서비스요청이있는지, tmdown 명령이왔는지를 check 한다. 4. 만약, 클라이언트로부터 LOGIN SERVICE 요청이온경우라인 40 을수행한다. 그렇지않는경우 tpschedule() 함수를빠져나온다. 5. 라인 56, tpgetclid() 를통해접속된 client 정보를 global 변수인 client_id 에저장한후 num_cli 값을증가시킨다. 6. 라인 61, LOGIN SERVICE 를요청한클라이언트에게응답데이터를전송한다. 7. 라인 36, tpschedule() 함수를빠져나온다. 8. 라인 30, num_cli 값이양의정수값이므로 for 문내를수행한다. 9. 라인 32, tpsendtocli() 를통해 client_id 에저장된클라이언트에게해당정보를전송한다.( 이부분이비요청메시지를전달하는부분이다.) 14
10. 3 ~ 9 을반복수행한다. 11. 만약 tpschedule() 에서 tmdown 명령을받았다면, tpsvrdone() 을수행한후프로세스가종료된다. 2.1 UCS 구성 2.1.1 UCS Server Program 구성 UCS Server Program 은다음과같은모듈과라이브러리로구성된다. $(TMAXDIR)/lib/libsvrucs.a 또는 $(TMAXDIR)/lib/shared/libsvrucs.so 1. Program 의 main() 과각종 ucs 관련 api 등을가지고있는라이브러리 2. UCS Program make 시항상 link 를해야한다. int tpsvrinit(int argc, char *argv[]) 1. 프로그램기동시한번수행된다. 대부분 Global 변수초기화나, non-xa 경우데이터베이스연결등이이곳에서구현된다. int tpsvrdone() 1. 프로그램종료시한번수행된다. 대부분사용 resource 반환이나 non-xa 경우데이터베이스연결해제등이이곳에서구현된다. int usermain(int argc, char *argv[]) 1. 실제 application 의 logic 이구현되는부분 2. 대부분무한 loop 형태로구현된다. (usermain() 모듈에서 return 되면, tpsvrdone() 을수행하고프로세스가종료되는형태이기때문이다.) 15
3. 클라이언트의요청데이터나 tmdown 명령을받기위해 tpschedule() 이항상필요하다. 만약 tpschedule() 이 call 되지않으면, tmdown 이되지않으며, 종료를원하는경우는 tmdown i 형태로종료해야한다. 그림 4 UCS Server Program 위와같이, UCS 프로그램은 1 개의모듈과 1 개의라이브러리로구성되며, usermain() 모듈내에는꼭 tpschedule() 함수를 call 해야한다. UCS Program 은위의프로그램구성을통해, User 의능동적제어가가능하며, 이를이용해다양한업무를수행할수있도록많은 API 를제공한다. ( 이 API 들의 prototype 은 $(TMAXDIR)/usrinc/ucs.h 에정의되어있다.) 주요 API 의 prototype 은다음과같다. 모든 UCS Program 에서사용되는 API 1. int tpschedule(int sec) 2. int tpuschedule(int usec) 16
비요청메시지전송에이용되는 API 1. int tpsendtocli(int clid, char *data, long len, long flags) 2. int tpgetclid(void) Non Tmax 와의비동기통신위한 API 1. int tpsetfd(void) 2. int tpissetfd(void) 3. int tpclrfd(void) 4. int tpgetctx(ctx_t *ctxp) 5. int tpcancelctx(ctx_t *ctxp) 6. CTX_T *tpsavectx() 7. int tprelay(char *svc, char *data, long len, long flags, CTX_T *ctxp); UCS 내에서 tpacall( 비동기통신 ) 함수사용시이용되는 callback API 1. int tpregcb(void) 2. int tpunregcb(void) 그러면위의각 API 들에대한사용방법및용도에대해설명은다음과같다. 2.1.1.1 int tpschedule(int timeout) 클라이언트 request 와 tmdown 명령등의이벤트를받아들이는함수로써, 최대 timeout 시간동안이벤트를기다린다. 만약 timout 시간까지어떤 이벤트도발생하지않으면, 함수리턴을한다. 이때리턴값은 -1 이다. 이때주의해야할사항은에러인경우도 1 이므로 tperrno 값이 판단기준이된다. 전자의경우 (timeout 인경우 ) 는 tperrno 의값이 13(TPETIME) 으로리턴된다. 입력값 : int sec 17
1. 양의정수 : 해당시간동안이벤트를기다린다. 2. 0 : 이벤트가발생할때까지무한정기다린다. 3. 1 : 도착한이벤트가있는지만을 check 하여, 있으면이벤트를발생시키고, 없으면함수리턴한다 출력값 : 없음 리턴값 : 예제 1. 양의정수 : 이벤트가도착했을경우를의미한다. 2. -1 : 이벤트가도착하지않았거나, 에러가발생한경우를의미한다. 이때, 주의할사항은이벤트가도착하지않는경우, 즉 timeout 인경우 tperrno 값에 13 이세팅되므로이값을 check 하여처리해야한다. #include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/ucs.h> int tpsvrinit(int argc, char *argv[]) return 1; int tpsvrdone() return 1; int usermain(int argc, char *argv[]) int rcode, i; 18
printf("usermain start\n"); while(1) rcode = tpschedule(1); printf( rcode = %d, i = %d, tperrno = %d\n, rcode, i, tperrno); i++; UCSTEST(TPSVCINFO *msg) /* tmax 의 SERVICE 부분 */ printf( UCSTEST start!!\n ); tpreturn(tpsuccess, 0, msg->data, msg->len, 0); 출력 : - 클라이언트 request 가없는경우 : rcode = -1, i = 0, tperrno = 13 rcode = -1, i = 1, tperrno = 13 rcode = -1, i = 2, tperrno = 13 - 클라이언트 request 가있는경우 rcode = -1, i = 0, tperrno = 13 rcode = -1, i = 1, tperrno = 13 rcode = -1, i = 2, tperrno = 13 UCSTEST start!! /* 클라이언트 request 도착 */ rcode =1, i=3, tperrno = 0 /* 1 을 return 받음 */ rcode = -1, i = 4, tperrno = 13 2.1.1.2 int tpuschedule(int usec) tpschedule() 과같은기능을수행하며, 틀린점은 microsecond 값이입력값으로사용된다. 19
2.1.1.3 int tpsendtocli(int clid, char *data, long len, long flags) clid 에해당하는클라이언트에게비요청메시지를전달하는함수이다. 여기서 clid 는 tpgetclid() 를통해얻어진다. 즉, tpsendtocli() 는 tpgetclid() 함수와함께사용되어야함을의미한다. 이함수를이용하여, 증권시세등과같이, 서버정보변화를클라이언트에게일방적으로전달하는형태의업무에사용된다. 입력값 1. int clid : tpgetclid() 로얻어지는클라이언트의유일한번호이다. 2. char *data : 전달할데이터이다. 반드시사용하기이전에 tpalloc 을통해할당된버퍼여야한다. 3. long len : 클라이언트에게전송할데이터의길이이다 4. long flags : 전송방법에대한구분자이다. 다음은 flags 에해당하는값에대한설명이다. a. 0 (Zero) 전송될데이터가반드시해당클라이언트에전송되어야할경우사용된다. 즉서버가매우바쁜경우해당데이터는저장되었다가순차적으로보내지게된다. 되는데이때, network 의 bandwidth 와처리량에따라계속적으로클라이언트에게보낼데이터가쌓여, 메모리 fault 문제가발생할수있다. b. TPUDP 위의 0 값의문제점이메모리증가에따른문제를 해결하기위한 flag 값으로써, 만약어는정도데이터가 쌓이면데이터를버려지게된다. 즉통신상의 UDP 와같이 클라이언트에게 전송될 데이터가 도중에 분실될 수도 있음을의미한다. c. TPFLOWCONTROL 클라이언트상태를점검하고데이터를전달할수있는지를확인한다음보내진다. 만약, 보낼수없는상태인경우 1 로함수리턴한다. 출력값 : 없음 리턴값 : 20
예제 1. 1 : 정상 2. 1 : 에러, tperrno 로어떤 error 인지를 check 해야한다. #include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/ucs.h> int client_id[5]; int usermain(int argc, char *argv[]) int jobs; int i; char *sndbuf; static int count = 0; printf("usermain start\n"); sndbuf = (char *)tpalloc("carray", NULL, 1024); while(1) for (i = 0; i < 5; i++) if (client_id[i] < 0) continue; sprintf(sndbuf, "Success tpsendtocli [%d] 012345678901234\n", count); tpsendtocli (client_id[i], sndbuf, 1024, 0); count++; jobs = tpschedule(1); CONN_CLI(TPSVCINFO *msg) 21
char int i; *sndbuf; sndbuf = (char *)tpalloc("carray", NULL, 1024); if (sndbuf == NULL) printf( tpalloc fail!!!\n, tperrno); tpreturn(tpfail, -1, NULL, 0, 0); for (i=0; i<5; i++) if (client_id[i] < 0) client_id[i] = tpgetclid(); break; strcpy(sndbuf, connect success!! ); tpreturn(tpsuccess, 0, (char *)sndbuf, strlen(sndbuf), 0); int tpsvrinit(int argc, char *argv[]) for (i=0; i<5; i++) client_id[i] = -1; return 1; int tpsvrdone() 2.1.1.4 int tpgetclid(void) reqeust 를요청한클라이언트의고유번호이다. 이번호는내부적으로 tmax 가부여한다. 이함수는비요청메시지를클라이언트에게전달하기위해 tpsendtocli() 와함께사용된다. tpsendtocli() 의첫번째 argument 인 clid 값은이함수를통해얻어지게된다. 22
입력값 : 없음 출력값 : 없음 리턴값 : request 를요청한클라이언트고유번호 2.1.1.5 int tpsetfd(int fd) 일반적으로 tpschedule() 에서는 Tmax 용클라이언트의 request 와 tmdown 의명령이도착했는지를 check 하게되는데, 이함수를이용하면이 fd 로온데이터도 check 하게된다. 이함수는네트워크프로그램에서많이사용되는 FD_SET() 과같은기능을가졌다고생각하면된다. 이함수는 tpclrfd(), tpissetfd() 등과함께사용된다. 입력값 : 1. int fd : socket fd 값 출력값 : 없음 리턴값 1. 1 : 정상 2.1.1.6 int tpissetfd(int fd) 2. 1 : 에러, tperrno 로에러를 check 한다. 해당 fd 로데이터가도착했는지를 check 한다. 이함수는네트워크프로그램에서많이사용되는 FD_ISSET() 과같은기능을가졌다고생각하면된다. 이함수는 tpsetfd(), tpclrfd() 등과함께사용된다. 입력값 1. int fd : socket fd 값 출력값 : 없음 리턴값 : 1. 1 : 정상 2. 1 : 에러, tperrno 로에러를 check 한다. 23
2.1.1.7 int tpclrfd(int fd) 해당 socket fd 를 tpschedule() 에서 check 하지않도록해제시키는함수이다. 이함수는네트워크프로그램에서많이사용되는 FD_CLR() 과같은기능을가졌다고생각하면된다. 이함수는 tpsetfd(), tpissetfd() 등과함께사용된다. 입력값 1. int fd : socket fd 값 출력값 : 없음 리턴값 예제 1. 1 : 정상인경우 2. 1 : 에러인경우, tperrno 로에러를 check 한다. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/types.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 <usrinc/atmi.h> #include <usrinc/ucs.h> #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff 24
#endif #define PORT 9345 #define HOST_ADDR "61.33.32.107" #define MAX_BUFFER_LEN 4096 +26 /* ------------------- global variable ------------------------- */ int client_fd = -1; +30 /* -- ---------------- service initial -------------------------- */ int tpsvrinit(int argc, char *argv[]) client_fd = network_connect(host_addr, PORT); if (client_fd > 0) /* connect 를통해얻어진 socket fd 를 tpschedule() 에등록한다. */ tpsetfd(client_fd); int tpsvrdone() if (client_fd > 0) /* tpschedule 로부터해당 socket fd 를해제한다. */ tpclrfd(client_fd); close(client_fd); /* ------------------- main --------------------------------- */ int usermain(int argc, char *argv[]) int n; +56 +57 /* never return */ while(1) 25
if (client_fd < 0) client_fd = network_connect(host_addr, PORT); if (client_fd > 0) tpsetfd(client_fd); else tpschedule(5); continue; if ((n = tpschedule(0)) < 0) sleep(1); continue; /* 해당 socket fd 로 data 가도착했는지를시험한다. */ if (tpissetfd(client_fd)) if ((n = request_from_client(client_fd)) < 0) tpclrfd(client_fd); close(client_fd); client_fd = -1; /* end of while */ /* ----------------------- client request ------------------------- -- */ int request_from_client(int fd) int n, len; 26
char *ptr, buffer[max_buffer_len]; /* read header */ memset(buffer, 0x00, sizeof(buffer)); n = socket_read(fd, buffer, 4); if (n <= 0) return -1; len = atoi(buffer); /* read data */ n = socket_read(fd, &buffer[4], len); if (n <= 0) return -1; sleep(3); len += 4; n = socket_write(fd, buffer, len); return n; /* ------------------- client connect for TCP/IP ------------------ */ int network_connect(char *host, int port) struct sockaddr_in serv_addr; unsigned long inaddr; struct hostent *hp; int i, fd; memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); if ((inaddr = inet_addr(host))!= INADDR_NONE) memcpy((char *) &serv_addr.sin_addr, (char *) &inaddr, sizeof(inaddr)); 27
else if ((hp = gethostbyname(host)) == NULL) return -1; memcpy((char *) &serv_addr.sin_addr, hp->h_addr, hp->h_length); 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; /* ------------------- data read ---------------------------------- */ int socket_read(int fd, char *ptr, int nbytes) int nleft, nread; char *ptr2; ptr2 = (char *)ptr; 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 */ 28
/* ------------------- data write --------------------------------- */ int socket_write(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); 2.1.1.8 int tpgetctx(ctx_t *ctxp) 비동기통신을위해, client 정보와 transaction 정보등을가져오는함수이다. 해당정보는 CTX_T structure 구조에담겨져온다. 이와비슷한동일한함수로는 tpsavectx() 가있으며이두함수의기능은정확히똑같다. 다만, ctxp 의 reference 를 user 가가지게되느냐, 아니면서버 library 내부에서가지느냐의차이만있을뿐이다. tpgetctx() 는 user 가 reference 를가지게된다. 대부분단일프로세스에서 ctxp 값을참조하고하는경우는 tpsavectx() 를사용하고, 둘이상의프로세스가이값을참조하고자하는경우는 tpgetctx() 를사용한다. tpgetctx() 를통하여 CTX_T * 를가져온경우 입력값 : 없음 출력값 29
1. CTX_T *ctxp : client 정보와 transaction 정보등으로이루어진 structure 구조 리턴값 1. 1 : 정상인경우 2. 1 : 에러인경우, tperrno 로에러를 check 한다. 2.1.1.9 int tpcancelctx(ctx_t *ctxp) 서버 library 내에가지고있는해당호출자의정보를지운다. 입력값 : 없음 출력값 1. CTX_T *ctxp : client 정보와 transaction 정보등으로이루어진 structure 구조 리턴값 1. 1 : 정상인경우 2. 1 : 에러인경우, tperrno 로에러를 check 한다. 2.1.1.10 CTX_T *tpsavectx() 비동기통신을위해, client 정보와 transaction 정보등을가져오는함수이다. 해당정보는 CTX_T structure 구조에담겨져온다. 이와동일한함수로는 tpgetctx() 가있으며이두함수의기능은정확히똑같다. 다만, ctxp 의 reference 를 user 가가지게되느냐, 아니면서버 library 내부에서가지느냐의차이만있을뿐이다. tpsavectx() 는서버 library 내부에서 reference 를가진다. 대부분단일프로세스에서 ctxp 값을참조하고하는경우는 tpsavectx() 를사용하고, 둘이상의프로세스가이값을참조하고자하는경우는 tpgetctx() 를사용한다. 입력값 : 없음 출력값 : 없음 리턴값 30
1. CTX_T *ctxp : client 정보와 transaction 정보등으로이루어진 structure 구조 2. NULL : 에러인경우 2.1.1.11 int tprelay(char *svc, char *data, long len, long flags, CTX_T *ctxp) 대외연동을위한비동기통신함수이다. 이함수는 tpforward() 를통해 UCS 의서비스요청이오게되면해당서비스에서는 tpsavectx() 를통해 client 정보와 transaction 정보를저장한후, 데이터를대외서버에보내게되고, 응답이오면 tpsavectx() 를통해저장된 client 정보와 transaction 정보를실어서또다른서비스를호출하게된다. 이를통해 UCS 프로그램이전혀 block 되는부분이없게된다. 일반적으로통신의효율을높이기위해송신서비스와수신서비스를분리하게되는데, 이때송신서비스는 tpforward() 를통해보내지고, 수신서비스는 tprelay() 를통해받게된다. 입력값 : 1. char *svc : 호출할 service 명 2. char *data : 보낼데이터 3. long len : 보낼데이터의길이 4. long flags 5. CTX_T *ctxp : client 정보와 transaction 정보등으로이루어진 structure 구조 출력값 : 없음 리턴값 예제 1. 1 : 정상 2. 1 : 에러 #include <stdio.h> 31
#include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/types.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 <usrinc/atmi.h> #include <usrinc/ucs.h> #ifndef INADDR_NONE #define INADDR_NONE #endif 0xffffffff #define MAX_CLIENT 10 #define PORT 9345 #define MAX_BUFFER_LEN 4096 struct myinfo_t int fd; char unique_id[10]; CTX_T *ctx; ; typedef struct myinfo_t MYINFO; /* ----------------------- global variable ------------------------ - */ 32
int listen_fd = -1; int client_fd = -1; int portno; MYINFO *myinfo; /* ----------------------- service initial ------------------------ -- */ int tpsvrinit(int argc, char *argv[]) listen_fd = network_listen(portno); if (listen_fd > 0) tpsetfd(listen_fd); client_init(); int tpsvrdone() if (listen_fd > 0) tpclrfd(listen_fd); close(listen_fd); /* ----------------------- client initial ------------------------- -- */ int client_init() int i, len; len = sizeof(myinfo) * MAX_CLIENT; myinfo = (MYINFO *)malloc(len); for (i = 0; i < MAX_CLIENT; i++) myinfo[i].fd = -1; 33
myinfo[i].ctx = NULL; /* ----------------------- main ----------------------------------- -- */ int usermain(int argc, char *argv[]) int n; /* never return */ while(1) if (listen_fd < 0) listen_fd = network_listen(port); if (listen_fd > 0) tpsetfd(listen_fd); else tpschedule(5); continue; if ((n = tpschedule(0)) < 0) sleep(1); continue; /* check listen port */ if (tpissetfd(listen_fd)) if ((client_fd = network_accept(listen_fd)) < 0) tpclrfd(listen_fd); close(listen_fd); listen_fd = -1; else tpsetfd(client_fd); 34
continue; /* check client port */ if (tpissetfd(client_fd)) if (client_response(client_fd) < 0) tpclrfd(client_fd); close(client_fd); client_fd = -1; /* end of while */ /* ----------------------- service -------------------------------- -- */ UCS_TEST(TPSVCINFO *msg) int i, n; CTX_T *ctx; printf("ucs_test : 12345\n"); ctx = (CTX_T *)tpsavectx(); for (i = 0; i < MAX_CLIENT; i++) if (myinfo[i].fd < 0) myinfo[i].fd = client_fd; memcpy(myinfo[i].unique_id, msg->data, 6); myinfo[i].ctx = ctx; break; n = message_write(client_fd, msg->data, msg->len); if (n < 0) tpclrfd(client_fd); close(client_fd); 35
client_fd = -1; tpreturn(tpfail, 999, (char *)NULL, 0, 0); tpreturn(tpsuccess, 0, (char *)NULL, 0, 0); /* ----------------------- message write -------------------------- -- */ int message_write(int fd, char *data, long datalen) int n, len; char buffer[max_buffer_len]; char temp[10]; memcpy(&buffer[4], data, datalen); sprintf(temp, "%04d", datalen); memcpy(buffer, temp, 4); len = datalen + 4; n = socket_write(fd, buffer, len); printf("ucs_listen:socket-write = %d\n", n); return n; /* ------------------- client response ---------------------------- */ int client_response(int fd) int i, n, len; CTX_T *ctx; char *ptr, buffer[max_buffer_len]; 36
/* read header */ memset(buffer, 0x00, sizeof(buffer)); n = socket_read(fd, buffer, 4); if (n <= 0) return -1; len = atoi(buffer); printf("ucs_listen:length : %d\n", len); /* client memory alloc */ ptr = (char *)tpalloc("carray", "", len+10); if (ptr == NULL) return -1; /* read data */ n = socket_read(fd, ptr, len); if (n <= 0) tpfree(ptr); return -1; for (i = 0; i < MAX_CLIENT; i++) if (myinfo[i].fd < 0) continue; if (memcmp(myinfo[i].unique_id, ptr, 6) == 0) ctx = myinfo[i].ctx; myinfo[i].fd = -1; myinfo[i].ctx = NULL; break; if (i >= MAX_CLIENT) return 0; printf("ucs_listen:tprelay before: length = %d\n", n); n = tprelay("xa_tolower", ptr, (long)n, 0, ctx); if (n < 0) 37
printf("ucs_listen:tprelay failed: tperrno=%d\n", tperrno); printf("ucs_listen:tprelay ok\n"); tpfree(ptr); return 0; /* ------------------- server : listen ---------------------------- */ int network_listen(int portno) int fd; int 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"); 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); 38
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); /* ------------------- data write --------------------------------- */ int socket_write(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 */ 39
nleft -= nwritten; ptr += nwritten; return(nbytes - nleft); /* ------------------- data read ---------------------------------- */ int socket_read(int fd, char *ptr, int nbytes) int nleft, nread; char *ptr2; ptr2 = (char *)ptr; 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 */ 40
2.1.1.12 int tpregcb(void) UCS 상에서비동기형요청에대한응답을받는루틴을서정하는함수이다. UCS 방식서버프로세스에서 tpgetreply() 대신사용될수있다. 된다. 즉, tpacall() 을통해보낸요청에대한응답을 tpgetreply() 를통해받는것이아니라 tpregcb() 에등록된 callback 함수를통해받게된다. 입력값 : 없음 출력값 : 없음 리턴값 1. 1 : 정상인경우 2. 1 : 에러인경우 2.1.2 UCS Client Program 구성 UCS Program 으로부터 Service 를제공받는 Client Program 의경우일반적인프로그램과거의유사한형식을가지게되는데, UCS Program 으로부터전송되는 Data 를수신할수있도록설정하는부분과, UCS Program 으로부터전송되는 Data 를처리하는부분이포함되어야한다. Client Program 에서사용하는함수로 UCS Program 으로부터전송되는 Data 를수신할수있도록설정하는것으로는 tpsetunsol_flag(), tpsetunsol(), tpgetunsol() 이있다. tpsetunsol_flag() 는 tpstart() 함수를이용하여 Tmax System 과연결시사용한 flag 값을재설정하는함수이다. 이함수는보통비요청메시지를받을것인지받지않을것인지의 flag 를설정한다. 그러면위의각 API 들에대한사용방법및용도에대해설명하자면다음과같다. 2.1.2.1 int tpsetunsol_flag(int flag) 비요청메시지수신 flag 를설정하는함수이다. tpsetunsol_flag() 는 tpstart() 함수를이용하여 Tmax 시스템과연결시사용한 flag 값을재설정하는함수이다. 이함수는보통비요청메시지의수신과관련된 41
flag 를설정한다. flag 는 sms TPUNSOL_IGN, TPUNSOL_HND, TPUNSOL_POLL 3 개중하나를입력한다. TPUNSOL_IGN flag 는비요청메시지를받지않겠다는의미이고, TPUNSOL_HND 과 TPUNSOL_POLL 은비요청메시지를받겠다는의미이다. 입력값 : int flag 1. TPUNSOL_IGN : 비요청메시지를받지않겠다는의미 2. TPUNSOL_HND : 비요청메시지를받는다는의미 3. TPUNSOL_POLL : 비요청메시지를받는다는의미 출력값 : 없음 리턴값 1. 1 : 정상인경우 2. 1 : 에러인경우 2.1.2.2 Unsolfunc tpsetunsol(void (*disp)(char *data, long len, long flags)) 비요청메시지수신시처리방법을설정하는함수이다. tpsetunsol() 은요청되지않은메시지가 Tmax 라이브러리에의해수신되었을때실행되어야할루틴을설정한다. tpsetunsol() 이처음으로호출되기전에, Tmax 라이브러리에의해수신된비요청메시지는무시된다. NULL 함수포인터의 tpsetunsol() 호출도마찬가지로무시된다. 시스템이비요청메시지를통지받는데사용되는방법은응용프로그램에임의로결정되며, 이는각클라이언트별로변경가능하다. tpsetunsol() 호출로전달된함수포인터는주어진인수정의에적합하여야한다. data 는수신된유형버퍼를지시하고, len 은데이터의길이를나타낸다. flags 는현재사용되지않는다. 만약아무데이터도수반되지않았다면, data 는 NULL 이될수있다. data 가클라이언트가알지못하는버퍼유형및하위유형인경우, 데이터는이해되지못한다. 응용프로그램 data 를제거할수없으며, 대신시스템이이를제거하고데이터영역을무효화하여 return 한다. 입력값 : 없음 42
출력값 : 없음 리턴값 1. 설정된비요청메시지처리루틴의포인터 : 정상적인경우 2. TPUNSOLERR : 에러인경우 2.1.2.3 int tpgetunsol(int type, char **data, long *len, long flags) 비요청메시지수신함수이다. tpgetunsol() 은클라이언트요청이없이일방적으로전달되어지는메시지를받아서처리하는함수이다. 이는메시지를보내는쪽에서 tpbroadcast() 또는 tpsendtocli(), tppost() 를통해전달한다. tpgetunsol() 호출전에전달된일방적인메시지들은무시된다. tpgetunsol() 을통하여비요청메시지를받으려면, tpstart() 를통해 Tmax 시스템에연결할때에 flags 를 TPUNSOL_POLL 이나 TPUNSOL_HND 로지정하여야한다. tpgetunsol() 이프로그램에호출되면 tpstart() 의플래그가 TPUNSOL_IGN 로설정되었다하더라도내부적으로 TPUNSOL_POLL 로전환되어서버로부터비요청메시지를받게된다. 입력값 : 1. int type a. UNSOL_TPPOST : tppost() 로비요청메시지가보내지는경우 b. UNSOL_TPBROADCAST : tpbroadcast() 로비요청메시지가보내지는경우 c. UNSOL_TPSENDTOCLI : tpsendtocli() 로비요청메시지가보내지는경우 2. int flags 출력값 a. TPBLOCK : 메시지가올때가지기다린다. b. TPNOTIME : 메시지가없으면바로리턴한다. 1. char **data : 수신받은데이터 43
2. long *len : 수신받은데이터의길이 리턴값 1. 1 : 정상인경우 2. 1 : 에러인경우 예제 #include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/ucs.h> main(int argc, char *argv[]) char *sndbuf; char *rcvbuf; long rcvlen; int RecvCnt = 0; if(tpstart((tpstart_t *)NULL) == -1) printf("tpstart failed[%s]\n",tpstrerror(tperrno)); exit(-1); tpsetunsol_flag(tpunsol_poll); if((sndbuf = (char *)tpalloc("carray", NULL, 1024)) == NULL) printf("sndbuf failed\n"); tpend(); exit(-1); if((rcvbuf = (char *)tpalloc("carray", NULL, 1024)) == NULL) printf("rcvbuf failed\n"); 44
tpfree((char *)sndbuf); tpend(); exit(-1); if(tpcall("login", sndbuf, 1024, &rcvbuf, &rcvlen, 0) == -1) printf("can't send request to service LOGIN\n"); tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); exit(1); rcvbuf); printf("after tpcall() received Message from server:%s\n", while(1) tpgetunsol(unsol_tpsendtocli, &rcvbuf, &rcvlen, TPBLOCK); printf("loop Count : %d\n", RecvCnt); if(rcvlen > 0) printf("counter : %d #[Received Data from Server : %s]\n ", RecvCnt, rcvbuf); RecvCnt ++; 2.1.3 RDP Server Program 구성 RDP Server Program 은기본적으로 UCS Program 에서와같이 usermain() 을통하여 application logic 을구현한다. RDP Server Program 은다음과같은모듈과라이브러리로구성된다. 45
$(TMAXDIR)/lib/libsvrrs. so 1. Program 의 main() 과각종 RDP 관련 api 등을가지고있는라이브러리 2. RDP Program compile 시항상 link 를해야한다. int tpsvrinit(int argc, char *argv[]) 1. 프로그램기동시한번수행된다. 대부분 Global 변수초기화나, non-xa 경우데이터베이스연결등이이곳에서구현된다. int tpsvrdone() 1. 프로그램종료시한번수행된다. 대부분사용 resource 반환이나 non-xa 경우데이터베이스연결해제등이이곳에서구현된다. int usermain(int argc, char *argv[]) 1. 실제 application 의 logic 이구현되는부분 2. 대부분무한 loop 형태로구현된다. (usermain() 모듈에서 return 되면, tpsvrdone() 을수행하고프로세스가종료되는형태이기때문이다.) 3. UCS Server Program 과는달리 RDP Server Program 에서는 tpschedule() 이필요하지않다 RDP Client Program 은 UCS Client Program 과같다. tpsetunsol_flag(), tpsetunsol(), tpgetunsol() 등의 API 를사용하여비요청메시지를받을수있도록프로그램을구현한다. 더자세한사항은 2.1.2 UCS Client Program 구성을참조하기바란다. 46
3 UCS 의환경 3.1 UCS 환경파일설정 환경파일의 SERVER 절에 SVGNAME 설정후 SVRTYPE 으로 UCS 를지정해주면된다. 다음은 UCS 환경파일을예로든것이다. *DOMAIN tmax1 SHMKEY =79970, MINCLH=1, MAXCLH=3, TPORTNO=8844, BLOCKTIME=120, RACPORT=3443 *NODE tmaxs1 MAXDIR = "/user1/jaya/tmax3511", APDIR = "/user1/jaya/tmax3511/appbin", PATHDIR = "/user1/jaya/tmax3511/path", TLOGDIR = "/user1/jaya/tmax3511/log/tlog", ULOGDIR = "/user1/jaya/tmax3511/log/ulog", SLOGDIR = "/user1/jaya/tmax3511/log/slog" tmaxs2 TMAXDIR = "/user/jaya/tmax3511", APPDIR = "/user/jaya/tmax3511/appbin", PATHDIR = "/user/jaya/tmax3511/path", TLOGDIR = "/user/jaya/tmax3511/log/tlog", ULOGDIR = "/user/jaya/tmax3511/log/ulog", SLOGDIR = "/user/jaya/tmax3511/log/slog" *SVRGROUP svg1 NODENAME = "tmaxs1" svg2 NODENAME = "tmaxs2" *SERVER ucssvr1 SVGNAME = svg1, SVRTYPE = UCS, CPC = 5 # UCS 와 CLH 간의채널수 ucssvr2 SVGNAME = svg2, 47
*SERVICE SVC1 SVRTYPE = UCS SVRNAME = ucssvr1 3.2 RDP 환경파일설정 RDP 를위한환경파일을만들기위해서는우선 DOMAIN 절의 MINCLH 의개수와 MAXCLH 개수를같게맞춰야한다. 그리고 NODE 절에 REALSVR 항목에 Real Server 명을써주어야한다. 그리고 rscpc 항목을설정해주어야한다. Real Server 는한노드에유일하며노드내의다른서버프로세스들도 Real Server 로데이터를전송후 Real Server 에서데이터를클라이언트로보내게된다. 각서버프로세스로부터서비스결과값은 Real Server 로보내지게되는데여기서쓰이는 Channel 수를 rscpc 에서설정해주어야한다. SERVER 절에서는 MIN 과 MAX 의개수를 DOMAIN 절의 MINCLH 와 MAXCLH 의개수보다많게설정해준다 ( 보통 2 배로설정한다 ). 그리고 SVRTYPE=REALSVR 라설정한다. *DOMAIN tmax1 *NODE tmaxi1 *SVRGROUP svg1 svg2 *SERVER deal real *SERVICE IN SHMKEY =70990, MINCLH=2, MAXCLH=2 TMAXDIR = "/home/navis/tmax", APPDIR = "/home/navis/tmax/appbin", PATHDIR = "/home/navis/tmax/path", TLOGDIR = "/home/navis/tmax/log/tlog", ULOGDIR = "/home/navis/tmax/log/ulog", SLOGDIR = "/home/navis/tmax/log/slog", REALSVR = real", rscpc = 16 NODENAME = "tmaxi1" NODENAME = "tmaxi1" SVGNAME = svg2, MIN=1 SVGNAME = svg1, MIN=1, MAX=1, SVRTYPE = REALSVR SVRNAME = deal 48
OUT SVRNAME = deal 3.3 UCS 컴파일방법 UCS Server Program 은컴파일시 UCS Library(libsvrucs.a 혹은 libsvrucs.so) 와링크되어야한다. 프로그램안에서도 $TMAXDIR/usrinc/ucs.h 가 include 되어야하고, Makefile 안에 TMAXLIBS 에반드시 -lsvrucs 를포함이용하여야한다. 다음은 UCS Server Program 을컴파일하기위한 Makefile 의예이다.( 예문의 OS 는 32bit Solaris 이며, OS 에따라 Makefile 내용은다를수있음 ) # Server makefile TARGET = $(COMP_TARGET) APOBJS = $(TARGET).o SDLFILE = demo.s #Solaris 의경우 LIBS = -lsvrucs -lsocket lnsl -nodb # 다른 OS 일경우 #LIBS = -lsvrucs -nodb OBJS = $(APOBJS) $(SDLOBJ) $(SVCTOBJ) SDLOBJ = $SDLFILE:.s=_sdl.o SDLC = $SDLFILE:.s=_sdl.c SVCTOBJ = $(TARGET)_svctab.o CFLAGS = -O -I$(TMAXDIR) APPDIR = $(TMAXDIR)/appbin SVCTDIR = $(TMAXDIR)/svct LIBDIR = $(TMAXDIR)/lib #.SUFFIXES :.c.c.o: $(CC) $(CFLAGS) -c $< # # server compile # 49
$(TARGET): $(OBJS) $(CC) $(CFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) mv $(TARGET) $(APPDIR)/. rm -f $(OBJS) $(APOBJS): $(TARGET).c $(CC) $(CFLAGS) -c $(TARGET).c $(SVCTOBJ): touch $(SVCTDIR)/$(TARGET)_svctab.c $(CC) $(CFLAGS) -c $(SVCTDIR)/$(TARGET)_svctab.c $(SDLOBJ): $(TMAXDIR)/bin/sdlc -i../sdl/$(sdlfile) $(CC) $(CFLAGS) -c../sdl/$(sdlc) # clean: -rm -f *.o core $(TARGET) 3.4 RDP 컴파일방법 RDP Server Program 은컴파일시 RDP Library(libsvrrs.so) 와링크되어야한다. 프로그램안에도 $TMAXDIR/usrinc/ucs.h 가 include 되어야하고, Makefile 안에 TMAXLIBS 에반드시 lsvrrs 와 lpthread 를포함이용하여야한다. 다음은 RDP Server ProgramUCS 을컴파일하기위한 Makefile 의예이다.( 예문의 OS 는 32bit Solaris 이며, OS 에따라 Makefile 내용은다를수있음 ) # Server makefile TARGET = $(COMP_TARGET) APOBJS = $(TARGET).o SDLFILE = demo.s #Solaris 의경우 LIBS = -lsvrrs -lpthread -lnodb -lsocket -lnsl OBJS = $(APOBJS) $(SDLOBJ) $(SVCTOBJ) SDLOBJ = $SDLFILE:.s=_sdl.o SDLC = $SDLFILE:.s=_sdl.c 50
SVCTOBJ = $(TARGET)_svctab.o CFLAGS = -O -I$(TMAXDIR) APPDIR = $(TMAXDIR)/appbin SVCTDIR = $(TMAXDIR)/svct LIBDIR = $(TMAXDIR)/lib #.SUFFIXES :.c.c.o: $(CC) $(CFLAGS) -c $< # # server compile # $(TARGET): $(OBJS) $(CC) $(CFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) mv $(TARGET) $(APPDIR)/. rm -f $(OBJS) $(APOBJS): $(TARGET).c $(CC) $(CFLAGS) -c $(TARGET).c $(SVCTOBJ): touch $(SVCTDIR)/$(TARGET)_svctab.c $(CC) $(CFLAGS) -c $(SVCTDIR)/$(TARGET)_svctab.c $(SDLOBJ): $(TMAXDIR)/bin/sdlc -i../sdl/$(sdlfile) $(CC) $(CFLAGS) -c../sdl/$(sdlc) # clean: -rm -f *.o core $(TARGET) 51
4 UCS 사용 UCS 는업무특성에따라다양하게프로그램화하여사용한다. 다음예들은실제사이트에서 UCS 가사용될때다양한방법으로프로그램화될수있다는것을보여준다. 4.1 비정상종료프로그램 (core) 발생시관리하는프로그램 ucs_svr1.c 프로그램은비정상종료프로그램 (core) 발생시해당 core file 를특정 directory 에 move 시키고이를연결된클라이언트에게비정상종료프로그램의정보를전달하는예제이다. #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <sys/time.h> #include <sys/types.h> #include <dirent.h> #include <fcntl.h> #include <usrinc/atmi.h> char _file_path[100]; int _cliid[10]; int tpsvrint(int argc, char *argv[]) return 0; int tpsvrdone() 52
return 0; int usermain(int argc, char *argv[]) int iret, i; char core_file[50]; char server_name[50]; char cur_time[20]; char *str; /* init cliid */ for (i=0; i<10; i++) _cliid[i] = -1; strcpy(_file_path, argv[1]); sprintf(core_file, "%s/core", _file_path); while(1) tpschedule(5); iret = checkcorefile(); if (iret == 1) iret = getcorename(core_file, server_name); iret = getcurrenttime(cur_time); iret = movecorefile(core_file, server_name, cur_time); str = (char *)tpalloc("string", NULL, 0); sprintf(str, "%s program core!!", server_name); for (i=0; i<10; i++) if (_cliid[i] < 0) continue; iret = tpsendtocli(_cliid[i], str, strlen(str), 0); if (iret == -1) printf("client close connect!!\n"); _cliid[i] = -1; for (i=0; i<10; i++) printf("cliid = %d - %d\n", i, _cliid[i]); tpfree(str); 53
int checkcorefile() char server_name[50]; char core_file[100]; struct dirent *dirent; DIR *dp; dp = opendir(_file_path); if (dp == NULL) printf("%s directory is not found!\n", _file_path); return -1; for (dirent = readdir(dp); dirent!= NULL; dirent = readdir(dp)) if ( (strlen(dirent->d_name) == 4) && (strncmp(dirent->d_name, "core", 4) == 0)) closedir(dp); return 1; closedir(dp); return -1; int getcorename(char *filename, char *server) int fd, cnt, i; char buf[6000]; fd = open(filename, O_RDONLY); #ifdef _HP cnt = read(fd, buf, 144); #endif #ifdef _SUN cnt = read(fd, buf, 132); #endif 54
#ifdef _IBM cnt = read(fd, buf, 1759); #endif cnt = read(fd, buf, 1760); while(1) cnt = read(fd, buf, 1); if (cnt!= 1) return -1; *server++ = buf[0]; if (buf[0] == 0) break; close(fd); int getcurrenttime(char *cur_time) struct tm *tm; time_t tnow; time(&tnow); tm = localtime(&tnow); sprintf(cur_time, "%04d%02d%02d%02d%02d%02d", tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); return 0; int movecorefile(char *core, char *server, char *time) char cmd[100]; char file_name[50]; printf("server = [%s]\n", server); sprintf(file_name, "core_%s_%s", server, time); sprintf(cmd, "mv %s %s/%s", core, _file_path, file_name); system(cmd); return 0; CONN_TERM(TPSVCINFO *msg) 55
int i; char *stdata; stdata = (char *)msg->data; for (i=0; i<10; i++) if (_cliid[i] >= 0) continue; _cliid[i] = tpgetclid(); printf("connect client %d = %d\n", i, _cliid[i]); break; tpreturn(tpsuccess, 0, (char *)stdata, 0, 0); 4.2 socket 을이용한비동기통신 ucs_svr2.c 프로그램은 client 에서들어오는 socket 을 accept 하고, accept 한 socket 에서들어오는요청을 tpacall 한후에그결과를다시 client 에전송하는예제이다. 다음은 ucs_svr2.c 의 usermain() 부분이다. #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/types.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 <usrinc/atmi.h> #include <usrinc/ucs.h> 56
#ifndef INADDR_NONE #define INADDR_NONE #endif 0xffffffff #define CLIENT_PORT 9345 #define HOST_ADDR "61.33.32.107" #define MAX_BUFFER_LEN 4096 /* ----------------------- global variable ------------------------ --- */ int client_fd = -1; char ip_addr[30]; int portno; extern int _cur_clhfd; /* ----------------------- service initial ------------------------ -- */ tpsvrinit(int argc, char *argv[]) sleep(5); parse_args(argc, argv); client_fd = network_connect(ip_addr, portno); if (client_fd > 0) tpsetfd(client_fd); printf("ucs_sample:client_fd = %d\n", client_fd); printf("ucs_sample: _cur_clhfd(1) = %d\n", _cur_clhfd); tpsvrdone() if (client_fd > 0) tpclrfd(client_fd); 57
close(client_fd); /* ----------------------- main ----------------------------------- -- */ int usermain(int argc, char *argv[]) int n; /* never return */ while(1) if (client_fd < 0) client_fd = network_connect(ip_addr, portno); if (client_fd > 0) tpsetfd(client_fd); else tpschedule(5); continue; printf("ucs_sample:client_fd = %d\n", client_fd); printf("ucs_sample: _cur_clhfd(1) = %d\n", _cur_clhfd); if ((n = tpschedule(0)) < 0) sleep(1); continue; printf("ucs_sample: _cur_clhfd(2) = %d\n", _cur_clhfd); if (tpissetfd(client_fd)) if ((n = request_from_client(client_fd)) < 0) tpclrfd(client_fd); close(client_fd); client_fd = -1; 58
/* ----------------------- command argument ----------------------- -- */ int parse_args(int argc, char *argv[]) int c; portno = -1; memset(ip_addr, 0x00, sizeof(ip_addr)); opterr = 0; /* don't want getopt() writing to stderr */ while ((c = getopt(argc, argv, "i:p:"))!= EOF) switch (c) case 'p': /* port */ portno = atoi(optarg); break; case 'i': /* ip-addr */ strcpy(ip_addr, optarg); break; case '?': printf("unrecognized option: -%c", optopt); /* default value: portno, shared memory key */ if (portno <= 0) portno = CLIENT_PORT; printf("no PORT is set: assumed %d\n", portno); if (ip_addr[0] == 0x00) strcpy(ip_addr, HOST_ADDR); 59
printf("no IP-ADDR is set: assumed %s\n", ip_addr); return 1; /* ----------------------- client request ------------------------- -- */ int request_from_client(int fd) int n, len; char *ptr, buffer[max_buffer_len]; /* read header */ memset(buffer, 0x00, sizeof(buffer)); n = socket_read(fd, buffer, 4); if (n <= 0) return -1; len = atoi(buffer); printf("ucs_sample:length : %d\n", len); /* read data */ n = socket_read(fd, &buffer[4], len); if (n <= 0) return -1; sleep(3); len += 4; n = socket_write(fd, buffer, len); printf("ucs_sample:socket write : n=%d\n", n); return n; 60
/* ------------------- client connect for TCP/IP ------------------ */ int network_connect(char *host, int port) struct sockaddr_in serv_addr; unsigned long inaddr; struct hostent *hp; int i, fd; memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); /* First try to convert the host name as a dotted-decimal number. * Only if that fails do we call gethostbyname(). */ if ((inaddr = inet_addr(host))!= INADDR_NONE) /* it's dotted-decimal */ memcpy((char *) &serv_addr.sin_addr, (char *) &inaddr, sizeof(inaddr)); else if ((hp = gethostbyname(host)) == NULL) printf("host name error: %s\n", host); return(-1); memcpy((char *) &serv_addr.sin_addr, hp->h_addr, hp->h_length); if ((fd = socket(af_inet, SOCK_STREAM, 0)) < 0) printf("can't open stream socket\n"); return -1; if (connect(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0) return fd; 61
close(fd); return -1; /* ------------------- data read ---------------------------------- */ int socket_read(int fd, char *ptr, int nbytes) int nleft, nread; char *ptr2; ptr2 = (char *)ptr; 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 */ /* ------------------- data write --------------------------------- */ 62
int socket_write(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); 4.3 RQ 를사용한프로그램 ucs_svr3.c 프로그램은 fail queue 에쌓인 data 를 dequeue 하여다시 call 하는프로그램예제이다. #include <sys/types.h> /* required for some of our prototypes */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include #include #include #include #include <sys/socket.h> <sys/stat.h> <sys/un.h> <netinet/in.h> <arpa/inet.h> 63
#include #include <usrinc/atmi.h> <usrinc/tmaxapi.h> #define MAX_BUF_SZ 10000 #define SZ 100 #define QUEFILE "rq1" #define QUESERVICE "+fail" int usermain(int argc, char*argv[]) char svc[xatmi_service_name_length]; long len, n; int try, ifailcnt; char *ptr; char QueFile[SZ]; char QueService[SZ]; strcpy(quefile, QUEFILE); strcpy(queservice, QUESERVICE); ptr = (char *)tpalloc("carray", NULL, MAX_BUF_SZ); while (1) /* Endless Loop */ tpschedule(10); /* Sleep 10 seconds */ n = 0; try = 0; ifailcnt = tpqstat(quefile, TMAX_FAIL_QUEUE); #ifdef _DEBUG printf("\ntpqstat FailCount value = [%d]\n", ifailcnt); #endif #ifdef _DEBUG while( (n >= 0) && (try++ < ifailcnt ) ) n = tpdeq(quefile, QueService, &ptr, &len, TPRQS ); 64
printf("tpdeq n value = [%s] \n", tpstrerror(n)); #endif if (n < 0 ) if (tperrno == TPEMATCH) /* Fail Q empty */ ; else printf("tpdeq fail[%s]\n", tpstrerror(tperrno)); continue; n = tpextsvcname((char*)ptr, svc); #ifdef _DEBUG printf("tpextsvcname value = [%s]\n", svc); #endif if (n < 0) printf("tpextsvcname fail![%s]\n", tpstrerror(tperrno)); continue; n = tpenq(quefile, svc, (char*)ptr, len, TPRQS); #ifdef _DEBUG printf("tpenq n value = [%d]\n", n); #endif if (n < 0) printf("tpenq fail![%s]\n", tpstrerror(tperrno)); continue; return 1; 65
5 부록 5.1 UCS 사례 UCS 가실제사이트에서사용되고있는사례이다. 다음은어떤업무에어떤목적으로 UCS 가사용되고있는지실사례를들어놓은것이다. 온라인티켓업무 1. RQ 에들어온요청을네트웍장애시메인 Tmax 서버로재전송을하기위한목적 POS 업무 1. RQ 에들어온요청을 DB 서버로재전송을하기위한목적 식음료서비스업무 1. 그룹차원에적용된 FireWall 에서일정시간이지난후클라이언트를 Disconnect 하기때문에, Applet 으로접속한클라이언트의 Connect 를유지시키기위해 Tmax 서버에서일정시간단위로 LOGIN 된클라이언트에게 Heart Bit 데이터를보내어 Session 을유지하기위한목적. HTS ( 사이버트레이딩업무 ) 1. 데이터베이스의정보를클라이언트들에게실시간으로전송하기위한목적. ARS 장비에서요구하는 request 를 socket 으로받아서 Tmax call 로변환하는업무 66
1. ARS 장비와 TCP/IP 로통신, ARS 에서전송한 Message 를 Tmax Server 에전달하고그결과를다시 ARS 에전송하기위한목적 2. 외부 Server 와 TCP/IP 통신, RQ 관련서버프로그램에 UCS 적용. 5.2 UCS 예제프로그램 5.2.1 간단한 UCS 예제프로그램 다음예는간단한 UCS 예제프로그램이다. 서버프로그램에서는 Loop 를수행하다가클라이언트에서 LOGIN 서비스를요청하면 UCS 가응답을보내준다. <<Config File>> *DOMAIN tmax1 *NODE tmaxs2 *SVRGROUP tmaxs2_nx *SERVER ucs_server *SERVICE LOGIN SHMKEY =79970, MINCLH=1, MAXCLH=3, TPORTNO=8844, BLOCKTIME=120 TMAXDIR = "/user/jaya/tmax3511", APPDIR = "/user/jaya/tmax3511/appbin", PATHDIR = "/user/jaya/tmax3511/path", TLOGDIR = "/user/jaya/tmax3511/log/tlog", ULOGDIR = "/user/jaya/tmax3511/log/ulog", SLOGDIR = "/user/jaya/tmax3511/log/slog" NODENAME = "tmaxs2" SVGNAME = tmaxs2_nx, SVRTYPE = UCS SVRNAME = ucs_server 67
5.2.1.1 Client Program Client Program 은실행을시키면 tpstart() 를하고 LOGIN 이라는서비스를 tpcall() 하여, UCS 프로세스에서비스를요청하게된다. Client Program 에설정된 tpsetunsol_flag(tpunsol_poll); 은서버프로세스에서보내는메시지를받겠다는뜻이다. Client Program 은서버프로세스에서 sndbuf 에담아보낸 "Client Registration Success" 라는메시지를 rcvbuf 에서받아출력한다. printf("after tpcall() received Message from server:%s\n", rcvbuf); Client 에서의요청이있으면서버프로세스의 loop count 가증가하고, count 가증가하면서서버프로세스의 for loop 에서 sndbuf 에담아보낸 Success tpsendtocli [0] 메시지가마찬가지로 Client 에서도 while loop 에서 Loop count 가증가하면서출력이된다. while(1) tpgetunsol(unsol_tpsendtocli, &rcvbuf, &rcvlen, TPBLOCK); printf("loop Count : %d\n", RecvCnt); if(rcvlen > 0) printf("counter : %d #[Received Data from Server : %s]\n ", RecvCnt, rcvbuf); RecvCnt ++; 다음은클라이언트프로그램의예문이다. <<Client Program>> #include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/ucs.h> main(int argc, char *argv[]) 68
char *sndbuf; char *rcvbuf; long rcvlen; int RecvCnt = 0; if(tpstart((tpstart_t *)NULL) == -1) error processing tpsetunsol_flag(tpunsol_poll); if((sndbuf = (char *)tpalloc("carray", NULL, 1024)) == NULL) error processing if((rcvbuf = (char *)tpalloc("carray", NULL, 1024)) == NULL) error processing if(tpcall("login", sndbuf, 1024, &rcvbuf, &rcvlen, 0) == -1) error processing rcvbuf); printf("after tpcall() received Message from server:%s\n", while(1) tpgetunsol(unsol_tpsendtocli, &rcvbuf, &rcvlen, TPBLOCK); printf("loop Count : %d\n", RecvCnt); if(rcvlen > 0) 69
printf("counter : %d #[Received Data from Server : %s]\n ", RecvCnt, rcvbuf); RecvCnt ++; if (RecvCnt == 10) break; tpfree((char *)sndbuf); tpfree((char *)rcvbuf); tpend(); 클라이언트프로그램예문의결과는다음과같다. tmaxs2:/user/jaya/tmax3511/sample/client> ucs_client After tpcall() received Message from server:client Registration Success Loop Count : 0 Counter : 0 #[Received Data from Server : Success tpsendtocli [0]] Loop Count : 1 Counter : 1 #[Received Data from Server : Success tpsendtocli [1]] Loop Count : 2 Counter : 2 #[Received Data from Server : Success tpsendtocli [2]] Loop Count : 3 Counter : 3 #[Received Data from Server : Success tpsendtocli [3]] Loop Count : 4 Counter : 4 #[Received Data from Server : Success tpsendtocli [4]] 5.2.1.2 Server Program Server Program 은 Tmax 가기동되고나서부터 5 초를 sleep 하고클라이언트에서요청이있을때까지 loop execute 0 을출력하며계속무한 loop 를하게된다. sleep(5); printf ("loop execute... %d\n", count); 70
무한 loop 를하다가 while loop 의마지막줄에있는 jobs = tpschedule(-1); 부분에서클라이언트에서보낸요청이있는지확인하고, 요청이없으면다시 loop 하지만요청이있을시에는그요청을받아들이게된다. 이때, client_id[num_cli] = tpgetclid(); printf("client id(clid) = %d\n", client_id[num_cli]); num_cli++; 저장된클라이언트의 id 값을가지고 while loop 의 for loop 에서는 count 수가증가하고그증가되는값을 sndbuf 에담아 tpsendtocli() 함수를사용하여클라이언트에게서비스를수행해준다. for (i = 0; i < num_cli; i++) sprintf(sndbuf, "Success tpsendtocli [%d]", count++); /* Client id 를참조하여고객에게데이타를보내는부분 */ tpsendtocli (client_id[i], sndbuf, 1024, 0); 그리고 sndbuf 에클라이언트가등록되었다는메시지를담아 tpreturn() 해준다. sprintf(sndbuf, "Client Registration Success"); tpreturn(tpsuccess, 0, (char *)sndbuf, 1000, 0); 다음은서버프로그램예문이다. <<Server Program>> #include <stdio.h> #include <usrinc/atmi.h> #include <usrinc/ucs.h> #define MAX_CLI 100 int num_cli; int client_id[max_cli]; int count; 71
tpsvrinit(int argc, char *argv[]) num_cli = 0; count = 0; printf("ucs Type Server tpsvrinit() is call\n"); /* tmax 의 ucs 모드에서 main 과같은부분 */ int usermain(int argc, char *argv[]) int jobs; int i; int ret; char *sndbuf; static int count = 0; printf("usermain start\n"); sndbuf = (char *)tpalloc("carray", NULL, 1024); while(1) sleep(5); printf ("loop execute... %d\n", count); for (i = 0; i < num_cli; i++) sprintf(sndbuf, "Success tpsendtocli [%d]", count++); /* Client id 를참조하여고객에게데이타를보내는부분 */ tpsendtocli (client_id[i], sndbuf, 1024, 0); jobs = tpschedule(-1); /* while 마지막에반드시있어야함 */ 72
LOGIN(TPSVCINFO *msg) char *sndbuf; int clid; int ret; int i; sndbuf = (char *)tpalloc("carray", NULL, 1024); if (num_cli < MAX_CLI) /* Client 의 id 값을보관하는부분 */ client_id[num_cli] = tpgetclid(); printf("client id(clid) = %d\n", client_id[num_cli]); num_cli++; sprintf(sndbuf, "Client Registration Success"); tpreturn(tpsuccess, 0, (char *)sndbuf, 1000, 0); tpsvrdone() 서버프로그램예문의결과는다음과같다. usermain start UCS Type Server tpsvrinit() is call usermain start loop execute... 0 loop execute... 0 loop execute... 0 client id(clid) = 2097152 loop execute... 0 loop execute... 1 loop execute... 2 loop execute... 3 loop execute... 4 73
loop execute... 5 loop execute... 6 loop execute... 7 loop execute... 8 loop execute... 9 loop execute... 10 loop execute... 11 loop execute... 12 loop execute... 13 loop execute... 14 client id(clid) = 2097153 loop execute... 15 loop execute... 17 loop execute... 19.. 5.2.2 서버대서버 UCS 프로그램 다음예문은 3 개의서버프로세스를이용하여하나의프로세스는 UCS 로서계속 Loop 하고, 다른하나는데이터베이스에서데이터를 select 하여또다른프로세스를 call 하여다른테이블에 insert 하는예제프로그램이다. 먼저 ucssvr.c 는 UCS 프로세스로서 mainsvr.pc 에있는 MAIN 이라는서비스를계속 tpcall 하는프로그램이다. UCS 프로세스로부터 tpcall 을받은 mainsvr.pc 는데이터베이스에있는 test_sel 이라는테이블에서첫번째데이터부터 select 하여 inssvr.pc 에있는 INS 라는서비스를 tpcall 한다. tpcall 을받은 inssvr.pc 는 test_ins 라는테이블에 test_sel 테이블에서 select 해온데이터를 insert 한다. 이과정이이루어지면 mainsvr.pc 는 tx_commit() 을하고 test_sel 테이블에서 inssvr.pc 에게넘겨준데이터를 delete 한다. test_sel 테이블에 6 개의데이터가있기때문에이런과정을 6 번 반복한다. 그리고나서는 delete 할데이터가없기때문에 1403 에러가난다. 74
<<Config File>> *DOMAIN tmax1 SHMKEY =79970, MINCLH=1, MAXCLH=3, TPORTNO=8844, BLOCKTIME=120 *NODE tmaxs2 TMAXDIR = "/user/jaya/tmax3511", APPDIR = "/user/jaya/tmax3511/appbin", PATHDIR = "/user/jaya/tmax3511/path", TLOGDIR = "/user/jaya/tmax3511/log/tlog", ULOGDIR = "/user/jaya/tmax3511/log/ulog", SLOGDIR = "/user/jaya/tmax3511/log/slog" *SVRGROUP tmaxs2_nx NODENAME = "tmaxs2" ### tms for Oracle ### tmaxs2_xa NODENAME = "tmaxs2", DBNAME = ORACLE, OPENINFO = "Oracle_XA+Acc=P/scott/tiger+SesTm=60", TMSNAME = tms_ora *SERVER ucssvr SVGNAME = tmaxs2_nx, SVRTYPE = UCS, MIN = 1 mainsvr SVGNAME = tmaxs2_xa, MIN = 1 inssvr SVGNAME = tmaxs2_xa, MIN = 1 *SERVICE MAIN SVRNAME = mainsvr INS SVRNAME = ins 75