Table of Contents
This chapter describes examples of TCP/IP gateway service types.
In this example, TCPGW is started up with Tmax. The remote node sends a request, and TCPGW calls the user-specified service. When the service completes processing the request and sends the result to TCPGW, TCPGW sends the result back to the remote node. This example is not related to TCPGW client/server method. Configure TCPGW by modifying custom.c according to the remote node environment.
The following diagram shows the operation of the OUTBOUND TCPGW program.
The program is composed of the following files.
Type | File Name |
---|---|
Configuration File | tcpgw.m |
TCPGW | custom.h, custom.c |
Remote Node | rclient.c, custom.h |
Server | svr.c |
*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
#ifndef _CUSTOM_H_ #define _CUSTOM_H_ /* -------------------------------------------------------------------- */ /* Fixed structures and macros */ #define MSG_MAGIC "Tmax" #define REMOTE_REQUEST 0 #define REMOTE_REPLY 1 #define SVC_NAME_LENGTH 20 /* msg_info_t struct must not be not redefined.*/ typedef struct msg_info { char svc[SVC_NAME_LENGTH]; int err; int len; int uid; int flags; int msgtype; int channel_id; } msg_info_t; /* -------------------------------------------------------------------- */ /* Modifiable structures and macros */ /* msg_header_t and msg_body_t structs can be redefined. */ typedef struct msg_header { int len; } msg_header_t; typedef struct msg_body { char name[16]; char data[100]; } msg_body_t; #endif
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/timeb.h> #include "custom.h" /* Since msg_header_size and comm_header_size are used in TCPGW library, they must be defined. */ int msg_header_size = sizeof(msg_header_t); int comm_header_size = 0; /* The internal logics do not have to implemented when the following functions are not required. */ /* However, they must be defined since they are used in the TCPGW library. */ int init_remote_info(char *myname, int mynumber, int num_channel, int key) { return 1; } int remote_connected(int index, int addr, int type, int fd) { return 1; } int get_msg_length(msg_header_t *hp) { if (hp == NULL || hp->len <= 0) return -1; /* Returns the actual data length. */ /* Reads the incoming data from the remote node using the return value. */ return ntohl(hp->len); } int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { msg_body_t *body; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; body = (msg_body_t *)data; info->len = ntohl(hp->len); info->err = 0; info->flags = 0; memset(info->svc, 0x00, SVC_NAME_LENGTH); strncpy(info->svc, body->name, 8); /* Return REMOTE_REQUEST to indicate that the request is from the remote node. */ return REMOTE_REQUEST; } int get_service_name(char *header, int err, char *svc) { return -1; } int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { msg_body_t *body; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; hp->len = htonl(info->len); body = (msg_body_t *)data; /* Send the error status in body->name */ if (info->err) /* error */ strcpy(body->name, "Fail"); else strcpy(body->name, "Success"); /* Return the data length needed to send result to the remote node. */ return (info->len + sizeof(msg_header_t)); } int get_channel_num(char *data) { return -1; } int put_msg_complete(char *hp, char *data, msg_info_t *info) { return 1; } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } int set_service_timeout(int uid, char *header) { return 1; } int chk_end_msg(int len, char *data) { return len; } int outmsg_recovery(char *data, msg_info_t *info) { return -1; } int inmsg_recovery(char *data, msg_info_t *info) { return -1; }
#This Makefile is for ibm 32bit. #TARGET must be same as the SERVER name defined in the Tmax environment file. TARGET = tcpgw APOBJS = $(TARGET).o #To create TCPGW, libtcpgw.a or libtcpgw.so must be linked. LIBS = -ltcpgw -ltmaxgw OBJS = custom.o register.o CFLAGS = -q32 -O -I$(TMAXDIR) -D_DBG LDFLAGS = -brtl APPDIR = $(TMAXDIR)/appbin LIBDIR = $(TMAXDIR)/lib .SUFFIXES : .c .c.o: $(CC) $(CFLAGS) $(LDFLAGS) -c $< $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) mv $(TARGET) $(APPDIR)/. rm -f $(OBJS) $(APOBJS): $(TARGET).c $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c
The following example is a remote node's client program for testing.
To test, execute TCPGW and then the program.
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include "custom.h" #define SERV_ADDR "168.126.185.132" #define SERV_PORT 5050 int main(int argc, char *argv[]) { int n, i, fd, ilen, olen; msg_header_t *header; msg_body_t *body, *result; /* (1) Connects to TCPGW. */ if ( (fd = network_connect()) < 0 ) { perror("network connect error:"); return -1; } /* (2) Configure the message. */ /* Message Header */ header = (msg_header_t *)malloc(sizeof(msg_header_t)); memset(header, 0x00, sizeof(msg_header_t)); header->len = sizeof(msg_body_t); /* Message Body */ body = (msg_body_t *)malloc(sizeof(msg_body_t)); memset(body, 0x00, sizeof(msg_body_t)); strncpy(body->name,"TOUPPER", 16); for (i = 0; i < 1; i++) { /* (3) Send data to TCPGW. */ if ( (n = send(fd, (char *)header, sizeof(msg_header_t), 0)) < 0 ) { perror("network write error:"); close(fd); return -1; } printf("Msg header send !\n"); /* Message Body */ strncpy(body->data, argv[1], 100); if ( (n = send(fd, (char *)body, sizeof(msg_body_t), 0)) <= 0 ) { perror("network write error:"); close(fd); return -1; } /* (4) TCPGW receives data. */ if ( (n = recv(fd, (char *)&olen, 4, 0)) < 0 ) { perror("network read error:"); close(fd); return -1; } result = (msg_body_t *)malloc(olen); memset((char *)result, 0x00, olen); if ( (n = recv(fd, (char *)result, olen, 0)) <= 0 ) { perror("network read error:"); close(fd); return -1; } } /* (5) Disconnect from TCPGW. */ close(fd); return 1; } int network_connect() { struct sockaddr_in serv_addr; int fd; memset((char *)&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR); serv_addr.sin_port = htons((unsigned short)SERV_PORT); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { return -1; } if (connect(fd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) >= 0) return fd; close(fd); return -1; }
#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); }
When a Tmax client (or a Tmax service) initiates a tpcall to TCPGW, TCPGW sends data to the remote node, and returns the result to the client/service when the remote node sends a reply.
It is important to specify the UID when a service is requested from the Tmax client to the remote node. Save the UID value (info->uid) specified in the TCPGW library in the message using the put_msg_info function, or save the user generated UID in the message and then set the UID value to the uid attribute. The message containing the UID is sent to the remote node, and the remote node must return the UID without modifying it. When TCPGW receives the reply, it calls the get_msg_info function to get the UID value and returns the reply back the client/service that requested the service.
The following example shows the operation of the synchronous inbound TCPGW program running as a client. To configure TCPGW, set the [-r], [-p], and [-n] options in the CLOPT item of the Tmax environment file and modify custom.c according to the remote node environment.
The program is composed of the following files.
Type | File Name |
---|---|
Environment File | tcpgw.m |
TCPGW | custom.h, custom.c |
Remote node | rserver.c, network.c |
Client | cli_tcpgw.c |
*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
#ifndef _CUSTOM_H_ #define _CUSTOM_H_ /* -------------------------------------------------------------------- */ /* Fixed structures and macros */ #define MSG_MAGIC "Tmax" #define REMOTE_REQUEST 0 #define REMOTE_REPLY 1 #define SVC_NAME_LENGTH 20 /* msg_info_t struct cannot be redefined. */ typedef struct msg_info { char svc[SVC_NAME_LENGTH]; int err; int len; int uid; int flags; int msgtype; int channel_id; } msg_info_t; /* -------------------------------------------------------------------- */ /* Modifiable structures and macros */ /* msg_header_t and msg_body_t structs can be redefined. */ #define UID_FIELD 98 #define SVC_NAME_FIELD 92 #define UID_LENGTH 4 #define SVC_LENGTH 6 typedef struct msg_header { int len; } msg_header_t; typedef struct msg_body { char data[92]; char name[6]; char uid[4]; } msg_body_t; #endif /* _CUSTOM_H_ */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/timeb.h> #include "custom.h" /* Since msg_header_size and comm_header_size are used in TCPGW library, they must be defined. */ int msg_header_size = sizeof(msg_header_t); int comm_header_size = 0; /* The internal logics do not have to implemented when the following functions are not required. */ /* However, they must be defined since they are used in the TCPGW library. */ int get_channel_num(char *data) { return -1; } int put_msg_complete(char *hp, char *data, msg_info_t *info) { return 1; } int get_service_name(char *header, int err, char *svc) { return -1; } int init_remote_info(char *myname, int mynumber, int num_channel, int key) { return 1; } int remote_connected(int index, int addr, int type, int fd) { return 1; } int get_msg_length(msg_header_t *hp) { if (hp == NULL || hp->len == 0){ return -1; } return ntohl(hp->len); } int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { info->flags = 0; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; info->len = ntohl(hp->len); info->err = 0; info->flags = 0; /* Configure info->uid. */ memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH); strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH); info->svc[SVC_LENGTH] = 0; if (info->uid == 0) { return REMOTE_REQUEST; } else { return REMOTE_REPLY; } } int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { int nSize = 0; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; /* The value entered in hp->len is the length of the actual data */ hp->len = htonl(info->len); if (info->err) { printf("info->err = %d\n", info->err); return -1; } else /* When making a request to the remote node, this uid */ /* must be set to the value configured in the library.*/ memcpy((char *)&data[UID_FIELD], (char *)&(info->uid), UID_LENGTH); memcpy((char *)&data[SVC_NAME_FIELD], info->svc, SVC_LENGTH); return (info->len + msg_header_size); } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } int set_service_timeout(int uid, char *header) { return 1; } int chk_end_msg(int len, char *data) { return len; } int outmsg_recovery(char *data, msg_info_t *info) { return -1; } int inmsg_recovery(char *data, msg_info_t *info) { return -1; }
#This makefile is for IBM 32bit. #TARGET must be the same as the SERVER anme defined in the Tmax environment file. TARGET = tcpgw APOBJS = $(TARGET).o #To generate TCPGW, libtcpgw.a or libtcpgw.s must be linked. LIBS = -ltcpgw -ltmaxgw OBJS = custom.o register.o CFLAGS = -q32 -O -I$(TMAXDIR) -D_DBG LDFLAGS = -brtl APPDIR = $(TMAXDIR)/appbin LIBDIR = $(TMAXDIR)/lib .SUFFIXES : .c .c.o: $(CC) $(CFLAGS) $(LDFLAGS) -c $< $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -L$(LIBDIR) -o $(TARGET) $(OBJS) $(LIBS) mv $(TARGET) $(APPDIR)/. rm -f $(OBJS) $(APOBJS): $(TARGET).c $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c
The following code is an example of a daemon program that can connect to TCPGW and send/receive messages.
For testing, TCPGW must be started while the program is running.
#include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include "custom.h" #define SERV_PORT 5060 void doit(int fd); int main(int argc, char *argv[]) { int ret, i, len; int fd, childfd; pid_t childpid; ret = network_listen(SERV_PORT); if (ret < 0){ return -1; }else fd = ret; for (;;){ childfd = network_accept(fd); if (childfd < 0){ return -1; } if ( (childpid = fork()) == 0){ close(fd); doit(childfd); exit(0); } close(childfd); } } void doit(int fd) { ssize_t n; int ret, i; msg_header_t *header; msg_body_t *body; header = (msg_header_t *)malloc(sizeof(msg_header_t)); memset((char *)header, 0x00, sizeof(msg_header_t)); body = (msg_body_t *)malloc(sizeof(msg_body_t)); memset((char *)body, 0x00, sizeof(msg_body_t)); readn2(fd, (char *)header, sizeof(msg_header_t)); readn2(fd, (char *)body, ntohl(header->len)); if (body->uid == 0){ printf("It's reply message\n"); }else printf("It's request message\n"); for (i = 0; i < sizeof(body->data); i++) body->data[i] = toupper(body->data[i]); header->len = htonl(sizeof(msg_body_t)); writen2(fd, (char *)header, sizeof(msg_header_t)); writen2(fd, (char *)body, sizeof(msg_body_t)); }
#include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff /* should be in <netinet/in.h> */ #endif /* ----------------------- extern global variables ------------------- */ extern int errno; extern char _svrname[30]; int network_listen(int portno) { int fd, on; struct sockaddr_in serv_addr; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ return(-1); } on = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0) { printf("setsockopt error for SO_REUSEADDR"); } #ifdef SO_KEEPALIVE on = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) { printf("setsockopt error for SO_KEEPALIVE"); } #endif memset((char *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(portno); if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { close(fd); return(-2); } if (listen(fd, 250) < 0) { close(fd); return(-3); } return(fd); } /* ------------------- server : client accept --------------------- */ int network_accept(int listenfd) { int fd, len; struct sockaddr_in cli_addr; len = sizeof(cli_addr); fd = accept(listenfd, (struct sockaddr *)&cli_addr, &len); if (fd < 0){ return(-1); /* often errno=EINTR, if signal caught */ } return(fd); } int readn2(int fd, char *ptr, int nbytes) { int nleft, nread; nleft = nbytes; while (nleft > 0) { nread = recv(fd, ptr, nleft, 0); if (nread < 0) { if (errno == EINTR) continue; else if (errno == EWOULDBLOCK) return (nbytes - nleft); return(nread); /* error, return < 0 */ } else if (nread == 0) break; /* EOF */ nleft -= nread; ptr += nread; } return (nbytes - nleft); /* return >= 0 */ } int writen2(int fd, char *ptr, int nbytes) { int nleft, nwritten; nleft = nbytes; while (nleft > 0) { nwritten = send(fd, ptr, nleft, 0); if (nwritten <= 0) { return(nwritten); /* error */ } nleft -= nwritten; ptr += nwritten; } return(nbytes - nleft); }
#This makefile is for compac. TARGET = rServer APOBJS = $(TARGET).o OBJS = $(APOBJS) network.o CFLAGS = -D_DBG .c.o: $(CC) $(CFLAGS) -c $< all : $(TARGET) $(TARGET):$(OBJS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) $(LIBS) rm -f $(OBJS)
The following is an example of a Tmax client requesting services to TCPGW. Execute the program after connecting TCPGW to the remote node.
#include <stdio.h> #include <usrinc/atmi.h> #include "custom.h" int main(int argc, char **argv) { int ret; msg_body_t *body; char *buf; long rlen; ret = tmaxreadenv("tmax.env", "TMAX"); if (ret < 0) { printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno)); } ret = tpstart((TPSTART_T *)NULL); if (ret < 0) { printf("tpstart fail...[%s]\n", tpstrerror(tperrno)); return -1; } buf = tpalloc("STRING", 0, 0); if (buf == NULL){ printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno)); tpend(); return -1; } body = (msg_body_t *)buf; memcpy(body->data, argv[1], strlen(argv[1])); body->data[51] = 0; /* Call the TCPGW service. */ ret = tpcall("TCPGW", buf, sizeof(msg_body_t), &buf, &rlen, 0); if (ret < 0){ printf("tpcall fail...[%s]\n", tpstrerror(tperrno)); tpfree((char *)buf); tpend(); return -1; } body = (msg_body_t *)buf; printf("return value = %s\n", body->data); tpfree((char *)buf); tpend(); }
The non-blocking TCPGW method is often used when communicating with external institutions. This is because even with a small number of processes, it incurs less load on the system and can process more tasks. A blocking method calls TCPGW directly from a Tmax client or service and receives a reply, but a non-blocking method uses separate sending and receiving processes to send a request to and process the reply from TCPGW.
All processes that want to call TCPGW must first call the sending process, and then the sending process pre-processes the request before calling TCPGW via tpforward. Before processing the request, TCPGW unblocks the channel connected to the Tmax engine and passes the request to the remote node. When TCPGW receives a reply from the remote node, it finds and forwards the reply to the receiving service to process the reply in order. After the receiving process processes the reply, it sends the result to the initiating service requester.
Since the service that directly calls TCPGW or the service that receives tprelay runs asynchronously, it incurs almost no overhead. However, the process that initially calls the sending service is blocked until it receives a reply.
The following diagram shows how a connection made between the remote node (server) and TCPGW (client).
The program is composed of the following files.
Type | File Name |
---|---|
Configuration File | tcpgw.m |
TCPGW | custom.c, custom.h |
Remote Node | rServer.c, network.c |
Server | sndsvr.c, rcvsvr.c |
Client | cli_tcpgw.c |
*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
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/timeb.h> #include "custom.h" /* Since msg_header_size and comm_header_size are used in TCPGW library, they must be defined. */ int msg_header_size = sizeof(msg_header_t); int comm_header_size = 0; /* The internal logics do not have to implemented when the following functions are not required. */ /* However, they must be defined since they are used in the TCPGW library. */ int get_channel_num(char *data) { return -1; } int put_msg_complete(char *hp, char *data, msg_info_t *info) { return 1; } /* Set the service name to tprelay to */ int get_service_name(char *header, int err, char *svc) { /* strcpy the internal user header (retsvcname). */ strcpy(svc, header); svc[8] = 0; return 1; } int init_remote_info(char *myname, int mynumber, int num_channel, int key) { return 1; } int remote_connected(int index, int addr, int type, int fd) { return 1; } int get_msg_length(msg_header_t *hp) { if (hp == NULL || hp->len == 0){ return -1; } return ntohl(hp->len); } /* get_msg_info and put_msg_info must be implemented same as the synchronous client TCPGW. */ int get_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { info->flags = 0; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; info->len = ntohl(hp->len); info->err = 0; info->flags = 0; /* Configure info->uid. */ memcpy((char *)&(info->uid), (char *)&data[UID_FIELD], UID_LENGTH); strncpy(info->svc, (char *)&data[SVC_NAME_FIELD], SVC_LENGTH); info->svc[8] = 0; if (info->uid == 0) { return REMOTE_REQUEST; } else { return REMOTE_REPLY; } } int put_msg_info(msg_header_t *hp, char *data, msg_info_t *info) { int nSize = 0; if ((info == NULL) || (hp == NULL) || (data == NULL)) return -1; /* The value in hp->len is the actual data length */ hp->len = htonl(info->len); if (info->err) { printf("info->err = %d\n", info->err); return -1; } else /* When a request is made to the remote node, this uid must be configured */ /* set to the value configured in the library.*/ memcpy((char *)&data[UID_FIELD], (char *)&(info->uid), 4); memcpy((char *)&data[SVC_NAME_FIELD], info->svc, 6); return (info->len + 4); } int remote_closed(int index, int type) { return 1; } int prepare_shutdown(int code) { return 1; } int set_service_timeout(int uid, char *header) { return 1; } int chk_end_msg(int len, char *data) { return len; } int outmsg_recovery(char *data, msg_info_t *info) { return -1; } int inmsg_recovery(char *data, msg_info_t *info) { return -1; }
Refer to the makefile example in "4.2. Synchronous INBOUND TCPGW".
The following example shows a daemon program that connects to TCPGW to sends/receives messages. For a test run, TCPGW must be started while this program is running.
#include <stdio.h> #include <unistd.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/un.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include "custom.h" #define SERV_PORT 5060 void doit(int fd); int main(int argc, char *argv[]) { int ret, i, len; int fd, childfd; pid_t childpid; ret = network_listen(SERV_PORT); if (ret < 0){ return -1; }else fd = ret; for (;;){ childfd = network_accept(fd); if (childfd < 0){ return -1; } if ( (childpid = fork()) == 0){ close(fd); doit(childfd); exit(0); } close(childfd); } } void doit(int fd) { ssize_t n; int ret, i; msg_header_t *header; /* Receives all message parts except the user header */ remote_body_t *body; int uid; header = (msg_header_t *)malloc(sizeof(msg_header_t)); memset((char *)header, 0x00, sizeof(msg_header_t)); body = (remote_body_t *)malloc(sizeof(remote_body_t)); memset((char *)body, 0x00, sizeof(remote_body_t)); readn2(fd, (char *)header, sizeof(msg_header_t)); readn2(fd, (char *)body, ntohl(header->len)); if (body->uid == 0){ printf("It's reply message\n"); }else printf("It's request message\n"); for (i = 0; i < sizeof(body->data); i++) body->data[i] = toupper(body->data[i]); header->len = htonl(sizeof(remote_body_t)); writen2(fd, (char *)header, sizeof(msg_header_t)); writen2(fd, (char *)body, sizeof(remote_body_t)); }
Refer to the network.c example in "4.2. Synchronous INBOUND TCPGW".
#include <string.h> #include <stdio.h> #include <usrinc/atmi.h> SENDSVC(TPSVCINFO *msg) { char *sndbuf; long len; printf("[%s] Service Started!\n", msg->name); len = (msg->len); printf("len is [%d]\n", len); if ((sndbuf = (char *)tpalloc("CARRAY", NULL, len)) == NULL) { printf("sndbuf alloc failed !\n"); tpreturn(TPFAIL, -1, NULL, 0, 0); } memcpy(sndbuf, msg->data, msg->len); /* Call TCPGW service via tpforward*/ tpforward("TCPGW", (char *)sndbuf, len, TPNOREPLY); }
#include <stdio.h> #include <string.h> #include <usrinc/atmi.h> /* TCPGW dynamically calls tprelay to this service. */ RECVSVC(TPSVCINFO *msg) { char *rcvbuf; long len; printf("[%s] Service Started!\n", msg->name); len = msg->len; rcvbuf = msg->data; if(tpurcode != 0) { printf("tpurcode is [%d] tperrmsg is [%s]\n", tpurcode, tpstrerror(tpurcode)); tpreturn( TPFAIL, -1,(char *)rcvbuf, len, 0 ); } tpreturn( TPSUCCESS, 0,(char *)rcvbuf, len, TPNOFLAGS ); }
The following example program uses a Tmax client to call SENDSVC. Before calling SENDSVC, configure the service to which TCPGW will tprelay to in the message header.
#include <stdio.h> #include <usrinc/atmi.h> #include "../server/custom.h" int main(int argc, char **argv) { int ret; msg_body_t *body; char *buf; long rlen; ret = tmaxreadenv("tmax.env", "TMAX"); if (ret < 0) { printf("tmaxreadenv fail...[%s]\n", tpstrerror(tperrno)); } ret = tpstart((TPSTART_T *)NULL); if (ret < 0) { printf("tpstart fail...[%s]\n", tpstrerror(tperrno)); return 0; } buf = tpalloc("STRING", 0, 0); if (buf == NULL){ printf("buf tpalloc fail..[%s]\n", tpstrerror(tperrno)); tpend(); return 0; } body = (msg_body_t *)buf; memcpy(body->data, argv[1], strlen(argv[1])); body->data[51] = 0; memcpy(body->retsvcname, "RECVSVC", 9); body->retsvcname[8] = 0; memcpy(buf, (char *)body, sizeof(msg_body_t)); /* Call the service that will call TCPGW */ ret = tpcall("SENDSVC", buf, sizeof(msg_body_t), &buf, &rlen, 0); if (ret < 0){ printf("tpcall fail...[%s]\n", tpstrerror(tperrno)); tpfree((char *)buf); tpend(); return 0; } body = (msg_body_t *)buf; printf("return value = %s\n", body->data); tpfree((char *)buf); tpend(); }