예제
본 장에서는 TCPGWTHR의 각 역할에 대한 예제를 설명한다.
1. 서버 모드 Outbound 서비스 호출
서버 모드 TCPGWTHR를 통해 리모트에서 Tmax의 서비스를 요청하는 예제이다.
프로그램 구성은 다음과 같다.
| 구분 | 파일명 |
|---|---|
환경 파일 |
tmax.m |
TCPGWTHR |
usermain.c |
리모트 노드 |
tcpcli1.c |
Tmax 노드 |
svr.c |
1.1. 환경 파일
< tmax.m >
*DOMAIN
res SHMKEY = 88000,
MINCLH = 1,
MAXCLH = 1,
TPORTNO = 8888
*NODE
node1TMAXDIR=”/home/tmax”,
APPDIR=”/home/tmax/appbin”
*SVRGROUP
svg1NODENAME = node1
*SERVER
tcpgwlsn SVGNAME = svg1, MIN=1, MAX=1, SVRTYPE = CUSTOM_GATEWAY, RESTART = N,
CLOPT="-- -P 9777 -N 3 -k 91000"
tcpgwhdr1 SVNAME = svg1, MIN=3, MAX=3, SCHEDULE=RR, SVRTYPE=CUSTOM_GATEWAY, CPC=10,
TARGET = tcpgwhdr,
CLOPT="-- -P 9777 -N 10 -s –L tcpgwlsn"
svr SVGNAME = svg1
*SERVICE
TESTSVC SVRNAME = svr
1.2. TCPGWTHR
<usermain.c >
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#else
#include <pthread.h>
#endif
#include <tcphdr.h>
int user_thrmain(WORKTHRINFO *winfo, int server)
{
if (server)
server_process(winfo);
else
client_process(winfo);
return 1;
}
int server_process(WORKTHRINFO *winfo)
{
int n, len, flags;
char tmp[10];
char sndbuf[4096];
char rcvbuf[4096];
long rcvlen;
int errflag;
memset(tmp, 0x00, sizeof(tmp));
while (1) {
n = tcpgw_select(winfo, 0, 0);
if (n < 0)
return -1;
printf("tcpgw_select: n = %d\n", n);
/* request ffrom tmax service & client */
switch(n) {
case WTHR_TMAX_REQUEST:
len = tcpgw_get_svcdata(winfo, &rcvbuf[4], &errflag, &flags);
if (len < 0) {
if (len == WTHR_CLIENT_CLOSE)
return -1;
printf("service data read failed\n");
return -1;
}
printf("TMAX_REQUEST: length = %d\n", len);
/* no reply */
if (flags)
n = tcpgw_tpreply(winfo, rcvbuf, n, 0);
sprintf(tmp, "%04d", len);
memcpy(rcvbuf, tmp, 4);
len += 4;
n = tcpgw_write(winfo->fd, rcvbuf, len);
if (n < 0) {
printf("remote client closed\n");
return -1;
}
printf("TMAX_REQUEST: remote write ok [%d]\n", n);
n = tcpgw_read(winfo->fd, tmp, 4, 0, 0);
if (n <= 0) {
printf("remote client closed\n");
return -1;
}
len = atoi(tmp);
if (len <= 0)
break;
printf("TMAX_REQUEST: read length = %d\n", len);
n = tcpgw_read(winfo->fd, rcvbuf, len, 0, 0);
if (n <= 0) {
printf("remote client closed\n");
return -1;
}
if (flags) {
printf("USER_REQUEST: service call length = %d\n", n);
flags = 1;
n = tcpgw_tpcall(winfo, "TESTSVC", rcvbuf, n, rcvbuf, &rcvlen);
}
else {
printf("TMAX_REQUEST: tpcall reply length = %d\n", n);
n = tcpgw_tpreply(winfo, rcvbuf, n, 0);
}
if (n < 0) {
printf("tmax down\n");
return -1;
}
break;
case WTHR_USER_REQUEST:
n = tcpgw_read(winfo->fd, tmp, 4, 0, 0);
if (n <= 0) {
printf("remote client closed\n");
return -1;
}
len = atoi(tmp);
if (len <= 0)
break;
printf("USER_REQUEST: read length = %d\n", len);
n = tcpgw_read(winfo->fd, sndbuf, len, 0, 0);
if (n <= 0) {
printf("remote client closed\n");
return -1;
}
printf("USER_REQUEST: service call length = %d\n", n);
n = tcpgw_tpcall(winfo, "TESTSVC", sndbuf, n, rcvbuf, &rcvlen);
if (n < 0) {
printf("service failed: [%d]\n", n);
}
sprintf(tmp, "%04d", rcvlen);
memcpy(sndbuf, tmp, 4);
memcpy(&sndbuf[4], rcvbuf, rcvlen);
len = rcvlen + 4;
printf("USER_REQUEST: service reply length = %d\n", len);
n = tcpgw_write(winfo->fd, sndbuf, len);
if (n < 0) {
printf("remote client closed\n");
return -1;
}
break;
case WTHR_SELECT_TIMEOUT:
/* timeout process */
break;
}
}
return 1;
}
int client_process(WORKTHRINFO *winfo)
{
int fd, portno;
char ipaddr[20];
portno = tcpgw_getaddr_from_winfo(winfo, ipaddr);
/* socket connect */
while (1) {
fd = tcpgw_network_connect(ipaddr, portno, 0);
if (fd > 0)
break;
printf("remote connect failed\n");
#ifdef _WIN32
Sleep(10000);
#else
sleep(10);
#endif
continue;
}
winfo->fd = fd; /* must save */
server_process(winfo);
return 1;
}
1.3. 리모트 노드
<tcpcli1.c >
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#ifdef _WIN32
#define GW_ADDR "host"
#else
#define GW_ADDR "10.10.10.10"
#endif
#define GW_PORT 9777
#define NUM_LOOP 1
#define MAX_MSG 496
#if (defined(_SOCK1) || defined(_SOCK11))
#define _LOBYTE 1
#define _HIBYTE 1
#else
#define _LOBYTE 2
#define _HIBYTE 0
#endif
#ifdef _WIN32
int winsock_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(_LOBYTE, _HIBYTE);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
printf("0060 Winsock startup error\n");
return -1;
}
/* Confirm that the WinSock DLL supports 2.0.*/
/* Note that if the DLL supports versions greater */
/* than 2.0 in addition to 2.0, it will still return */
/* 2.0 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != _LOBYTE ||
HIBYTE(wsaData.wVersion) != _HIBYTE) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
WSACleanup();
printf("0061 Winsock version check error\n");
return -1;
}
/* The WinSock DLL is acceptable. Proceed. */
return 1;
}
#endif
int _network_connect(char *host, int port)
{
struct sockaddr_in serv_addr;
unsigned int 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 = (unsigned int) inet_addr(host)) != -1) {
memcpy((char *) &serv_addr.sin_addr, (char *) &inaddr,
sizeof(struct in_addr));
} else {
if ((hp = gethostbyname(host)) == NULL) {
printf("COM3412: host name error: %s ", 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("COM3413: can't open stream socket");
return -1;
}
if (connect(fd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) >= 0)
return fd;
close(fd);
return -1;
}
int main(int argc, char *argv[])
{
char gw_addr[256], tmp[10];
int gw_port, fd, i, n, len, num_loop;
char data[MAX_MSG];
strcpy(gw_addr, GW_ADDR);
gw_port = GW_PORT;
num_loop = NUM_LOOP;
if (argc == 2) {
num_loop = atoi(argv[1]);
} else if (argc == 3) {
gw_port = atoi(argv[1]);
num_loop = atoi(argv[2]);
} else if (argc >= 4) {
strcpy(gw_addr, argv[1]);
gw_port = atoi(argv[2]);
num_loop = atoi(argv[3]);
}
#ifdef _WIN32
winsock_init();
#endif
fd = _network_connect(gw_addr, gw_port);
if (fd < 0) {
printf("Connect to (%s:%d) fail \n", gw_addr, gw_port);
return -1;
}
sleep(5);
memset(data, 0x00, MAX_MSG);
for (i=0; i<num_loop; i++) {
sprintf(&data[4], "Msg(%d) produced by PID(%d)", i, getpid());
len = strlen(&data[4]);
sprintf(tmp, "%04d", len);
memcpy(data, tmp, 4);
len += 4;
n = send(fd, data, len, 0);
if (n != len) {
printf("Sent only %d / %d bytes\n", n, len);
return -1;
}
len = 4;
n = recv(fd, data, len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return -1;
}
memcpy(tmp, data, 4);
tmp[4] = 0x00;
len = atoi(tmp);
if (len <= 0) {
printf("Pid (%d) received %d bytes\n", getpid(), n);
continue;
}
n = recv(fd, data, len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return -1;
}
printf("Pid (%d) received %d bytes\n",
getpid(), n);
sleep(5);
}
return 1;
}
2. 서버 모드 Inbound 서비스 호출
서버 모드 TCPGWTHR를 통해 Tmax에서 리모트의 서비스를 요청하는 예제이다.
프로그램 구성은 다음과 같다.
| 구분 | 파일명 |
|---|---|
환경 파일 |
tmax.m, tcpgwthr.cfg |
TCPGWTHR |
usermain.c (파일의 예제는 서버 모드 Outbound 서비스 호출에서 설명했으므로 본 절에서는 생략한다.) |
리모트 노드 |
tcpcli2.c |
Tmax 노드 |
toupper.c |
2.1. 환경 파일
< tmax.m >
*DOMAIN
Res SHMKEY = 88000,
MINCLH = 1,
MAXCLH = 1,
TPORTNO = 8888
*NODE
node1TMAXDIR=”/home/tmax”,
APPDIR=”/home/tmax/appbin”
*SVRGROUP
svg1NODENAME = node1
*SERVER
tcpgwlsn SVGNAME = svg1, MIN=1, MAX=1, SVRTYPE = CUSTOM_GATEWAY, RESTART = N,
CLOPT="-- -P 9777 -N 3 -k 91000 -F /home/tmax/appbin/tcpgwthr.cfg"
tcpgwhdr1 SVNAME = svg1, MIN=3, MAX=3, SCHEDULE=RR,SVRTYPE=CUSTOM_GATEWAY, CPC= 10,
TARGET = tcpgwhdr,
CLOPT="-- -P 9777 -N 10 -s –L tcpgwlsn"
*SERVICE
svcgw SVRNAME = tcpgwhdr1
< tcpgwthr.cfg >
# Client IP Server Port Client ID 10.10.10.10 9777 SVR0001
2.2. 리모트 노드
< tcpcli2.c >
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#ifdef _WIN32
#define GW_ADDR "host"
#else
#define GW_ADDR "10.10.10.10"
#endif
#define GW_PORT 9777
#define NUM_LOOP 1
#define MAX_MSG 496
#if (defined(_SOCK1) || defined(_SOCK11))
#define _LOBYTE 1
#define _HIBYTE 1
#else
#define _LOBYTE 2
#define _HIBYTE 0
#endif
#ifdef _WIN32
int winsock_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(_LOBYTE, _HIBYTE);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
printf("0060 Winsock startup error\n");
return -1;
}
/* Confirm that the WinSock DLL supports 2.0.*/
/* Note that if the DLL supports versions greater */
/* than 2.0 in addition to 2.0, it will still return */
/* 2.0 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != _LOBYTE ||
HIBYTE(wsaData.wVersion) != _HIBYTE) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
WSACleanup();
printf("0061 Winsock version check error\n");
return -1;
}
/* The WinSock DLL is acceptable. Proceed. */
return 1;
}
#endif
int _network_connect(char *host, int port)
{
struct sockaddr_in serv_addr;
unsigned int 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 = (unsigned int) inet_addr(host)) != -1) {
memcpy((char *) &serv_addr.sin_addr, (char *) &inaddr,
sizeof(struct in_addr));
} else {
if ((hp = gethostbyname(host)) == NULL) {
printf("COM3412: host name error: %s ", 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("COM3413: can't open stream socket");
return -1;
}
if (connect(fd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) >= 0)
return fd;
close(fd);
return -1;
}
int main(int argc, char *argv[])
{
char gw_addr[256], tmp[10];
int gw_port, fd, i, n, len, num_loop;
char data[MAX_MSG];
strcpy(gw_addr, GW_ADDR);
gw_port = GW_PORT;
if (argc == 2) {
gw_port = atoi(argv[1]);
}
#ifdef _WIN32
winsock_init();
#endif
fd = _network_connect(gw_addr, gw_port);
if (fd < 0) {
printf("Connect to (%s:%d) fail \n", gw_addr, gw_port);
return -1;
}
memset(data, 0x00, MAX_MSG);
while (1) {
len = 4;
n = recv(fd, data, len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return -1;
}
memcpy(tmp, data, 4);
tmp[4] = 0x00;
len = atoi(tmp);
if (len <= 0) {
printf("Pid (%d) received %d bytes\n", getpid(), n);
continue;
}
n = recv(fd, &data[4], len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return -1;
}
printf("Pid (%d) received %d bytes\n", getpid(), n);
len = n + 4;
for (i = 4; i < len; i++)
data[i] = toupper(data[i]);
n = send(fd, data, len, 0);
if (n != len) {
printf("Sent only %d / %d bytes\n", n, len);
return -1;
}
printf("Pid (%d) sended %d bytes\n", getpid(), len-4);
}
return 1;
}
2.3. Tmax 노드
< toupper.c >
#include <stdlib.h>
#include <string.h>
#include <usrinc/atmi.h>
#include <usrinc/hlinkapi.h>
main(int argc, char *argv[])
{
char *sndbuf, *rcvbuf;
long rcvlen, sndlen;
int ret, len;
TPGWINFO_T gwinfo;
strcpy(gwinfo.svc, "SVR0001");
if (argc != 2) {
printf("Usage: toupper string\n");
exit(1);
}
if ( (ret = tmaxreadenv( "tmax.env","TMAX" )) == -1 ){
printf( "tmax read env failed\n" );
exit(1);
}
if (tpstart((TPSTART_T *)NULL) == -1){
printf("tpstart failed\n");
exit(1);
}
if ((sndbuf = (char *)tpalloc("CARRAY", NULL, 0)) == NULL) {
printf("sendbuf alloc failed !\n");
tpend();
exit(1);
}
if ((rcvbuf = (char *)tpalloc("CARRAY", NULL, 0)) == NULL) {
printf("recvbuf alloc failed !\n");
tpfree((char *)sndbuf);
tpend();
exit(1);
}
memcpy(sndbuf, &gwinfo, TPGWINFO_SIZE);
strcpy(sndbuf+TPGWINFO_SIZE, argv[1]);
len = strlen(argv[1]);
len += TPGWINFO_SIZE;
if(tpcall("svcgw", sndbuf, len, &rcvbuf, &rcvlen, 0)==-1){
printf("Can't send request to service svcgw - %d\n", tperrno);
tpfree((char *)sndbuf);
tpfree((char *)rcvbuf);
tpend();
exit(1);
}
printf("send data: %s\n", sndbuf+TPGWINFO_SIZE);
printf("recv data: %s\n", rcvbuf);
tpfree((char *)sndbuf);
tpfree((char *)rcvbuf);
tpend();
}
3. 클라이언트 모드 Inbound 서비스 호출
클라이언트 모드 TCPGWTHR을 통해 Tmax에서 리모트의 서비스를 요청하는 예제이다.
프로그램 구성은 다음과 같다.
| 구분 | 파일명 |
|---|---|
환경 파일 |
tmax.m, tcpgwthr.cfg |
TCPGWTHR |
usermain.c (파일의 예제는 서버 모드 Outbound 서비스 호출에서 설명했으므로 본 절에서는 생략한다.) |
리모트 노드 |
tcpsvr2.c |
Tmax 노드 |
toupper.c (파일의 예제는 서버 모드 Inbound 서비스 호출에서 설명했으므로 본 절에서는 생략한다.) |
3.1. 환경 파일
< tmax.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
clihdrshm SVGNAME = svg1, MIN = 1, MAX = 1,
SVRTYPE = CUSTOM_GATEWAY,
CLOPT = "-- -N 10 -k 91000 -F /home/tmax/appbin/tcpgwthr.cfg"
tcpgwhdr1 SVGNAME svg1, MIN = 10, MAX = 10, SVRTYPE = CUSTOM_GATEWAY,
CPC = 9, SCHEDULE = RR, TARGET = tcpgwhdr,
CLOPT = "-- -k 91000 –L clihdrshm -F /home/tmax/appbin/tcpgwthr.cfg"
*SERVICE
svcgw SVRNAME = tcpgwhdr1
3.2. 리모트 노드
< tcpsvr2.c >
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#define GW_PORT 9777
#define NUM_LOOP 1
#define MAX_MSG 496
#if (defined(_SOCK1) || defined(_SOCK11))
#define _LOBYTE 1
#define _HIBYTE 1
#else
#define _LOBYTE 2
#define _HIBYTE 0
#endif
#define max(a,b) ((a) > (b) ? (a) : (b))
int listen_fd = -1;
int work_fd;
fd_set readfds;
int maxfd = 0;
#ifdef _WIN32
int winsock_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(_LOBYTE, _HIBYTE);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
printf("0060 Winsock startup error\n");
return -1;
}
/* Confirm that the WinSock DLL supports 2.0.*/
/* Note that if the DLL supports versions greater */
/* than 2.0 in addition to 2.0, it will still return */
/* 2.0 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != _LOBYTE ||
HIBYTE(wsaData.wVersion) != _HIBYTE) {
/* Tell the user that we couldn't find a usable */
/* WinSock DLL. */
WSACleanup();
printf("0061 Winsock version check error\n");
return -1;
}
/* The WinSock DLL is acceptable. Proceed. */
return 1;
}
#endif
int network_listen(int portno)
{
int fd;
struct sockaddr_in serv_addr;
int on;
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("0062 setsockopt error: 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((unsigned short)portno);
if (bind(fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
close(fd);
return(-2);
}
if (listen(fd, 50) < 0) {
close(fd);
return(-3);
}
return(fd);
}
int network_accept(int listenfd)
{
socklen_t len;
int fd, on;
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 main(int argc, char *argv[])
{
char gw_addr[256];
int gw_port, fd, i, n, len, num_loop;
char data[MAX_MSG], *sndbuf;
gw_port = GW_PORT;
if (argc == 2) {
gw_port = atoi(argv[1]);
}
#ifdef _WIN32
winsock_init();
#endif
listen_fd = network_listen(gw_port);
if (listen_fd < 0) {
printf("Connect to (%d) fail \n", gw_port);
return -1;
}
FD_ZERO(&readfds);
FD_SET(listen_fd, &readfds);
maxfd = listen_fd;
while (1) {
errno = 0;
n = select(maxfd + 1, &readfds, NULL, NULL, NULL);
if (n < 0) {
if (errno == EINTR) { /* signal is caught */
continue;
} else {
printf("0080 select error");
return -1;
}
}
if (FD_ISSET(listen_fd, &readfds)) {
if ((fd = network_accept(listen_fd)) < 0) {
printf("0043 socket accept error");
continue;
}
else {
work_fd = fd;
FD_SET(fd, &readfds);
maxfd = max(maxfd, fd);
}
}
if (FD_ISSET(work_fd, &readfds)) {
service_process(work_fd);
FD_CLR(work_fd, &readfds);
close(work_fd);
}
}
return 1;
}
int service_process(int fd)
{
int i, n, len;
char tmp[10];
char data[MAX_MSG];
len = 4;
n = recv(fd, data, len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return -1;
}
memcpy(tmp, data, 4);
tmp[4] = 0x00;
len = atoi(tmp);
if (len <= 0) {
printf("Pid (%d) received %d bytes\n", getpid(), n);
return 1;
}
n = recv(fd, &data[4], len, 0);
if (n != len) {
printf("Recv error %d\n", n);
return 1;
}
printf("Pid (%d) received %d bytes\n", getpid(), n);
len = n + 4;
for (i = 4; i < len; i++)
data[i] = toupper(data[i]);
n = send(fd, data, len, 0);
if (n != len) {
printf("Sent only %d / %d bytes\n", n, len);
return 1;
}
printf("Pid (%d) sended %d bytes\n", getpid(), len-4);
return 1;
}