#include "xml_tcp.h"
/*
* 1. Default, socket recv and send are blocking mode.
* 2. Use CSV (comma seperate value) to distinguish tracking_numbers.
* 3. SO_RCVBUF 65535, SO_SNDBUF 65535
* 4. EWOULDBLOCK, EPIPE
* 5. getopt()
* 6. use 'kill -USR1 4720' to terminate safely this daemon service?
* 7. 'send() return 0' == EPIPE?
*/
static const char rcsid[] = "$Id$";
int main(int argc, char *argv[])
{
int s, ns, pid, c, param_flag = 0;
struct sockaddr_in sin;
int ret, state = 1;
time_t tv, tv1, tv2;
char *server_port=SERVER_PORT_1, tmp[26];
if( argc > 1 ) {
while( 1 ) {
c = getopt(argc, argv, "has:");
if(c == -1) {
break;
}
else {
switch(c) {
case 'h':
get_help();
break;
case 's':
// initialize server_port.
server_port = optarg;
break;
case 'a':
param_flag = 1;
break;
default:
// default server entry is: :146.18.58.25:9100;
// server_port = SERVER_PORT;
break;
}
}
} //end while
} //end argc
daemon_init ();
/*
* Create a log file to keep error and warning message.
*/
//fp = fopen("/home/prod/xmlcst_tcp.log", "w");
fp = fopen("/dev/stderr", "w");
if( NULL == fp ) {
fatal(__LINE__, "fopen");
}
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fatal(__LINE__, "socket");
}
/*
* If server crashes or exits and then tries to restart within 2 minutes,
* when binding is attempted, an error will be returned indicating
* "address alrady in use."
* By using SO_REUSEADDR option, a server can bind to the address regardless
* of thestate of the post.
* Avoid TIME-WAIT on this socket and allow it to be immediately reused.
*/
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&state, sizeof(state) );
/*
* Normally, when there is no data transmission on a TCP socket, there is
* no packet dialog either. The peer could disappear and the socket would
* be declared dead only after data data is attempted to be sent.
* SO_KEEPALIVE routinely probe the peer to ensure it's still there. A probe
* will either result in an ACK being returned from the peer (it's alive),
* no response (host is gone?) or an RST packet indicating that the peer has
* closed its end of the socket.
* SO_KEEPALIVE 2 hours perform the probe.
*/
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&state, sizeof(state) );
/*
* how a socket is closed for a connection-based socket.
* without it, when a socket is closed, the stack will continue to try
* to delivery any unsent data to the peer.
*/
lingvar.l_onoff = 1;
lingvar.l_linger = 60; //seconds
setsockopt( s, SOL_SOCKET, SO_LINGER, (void *)&lingvar, sizeof(struct linger) );
//state = 0;
// Disable the Nagle algorithm(commonly it's enabled by default)
//setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void*)&state, sizeof(state));
if( param_flag )
get_socket_params(s);
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORTNUM);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *) & sin, sizeof(sin)) == -1) {
fatal(__LINE__, "bind");
}
if (listen(s, 10) == -1) {
fatal(__LINE__, "listen");
}
ret = sizeof(struct sockaddr_in);
getsockname(s, (struct sockaddr *)&sin, (int*)&ret);
sleep( 2 );
time(&tv);
strcpy(tmp, ctime(&tv) + 4);
printf("\nStart up Time: %s", tmp);
printf("XML Agent Server has been startup successfully...\n");
printf("See log file /home/prod/xmlcst_tcp.log for details!\n");
printf("============================================\n");
sprintf(warning_str, "The Server IP Address %s, port %d",
inet_ntoa(sin.sin_addr), ntohs(sin.sin_port) );
warns( __LINE__, warning_str );
while (1) {
struct sockaddr_in client;
int client_size;
int i = 0;
client_size = sizeof(struct sockaddr_in);
ns = accept(s,(struct sockaddr *)&client,(int *)&client_size);
if (ns == -1) {
// The accept attempt was interrupted by the delivery of a signal
if(errno == EINTR)
continue; /* Back to for()... */
else {
fatal(__LINE__, "accept");
}
}
sprintf( warning_str, "Getting a connection from ",
inet_ntoa(client.sin_addr));
ret = sizeof(struct sockaddr_in);
getpeername(s, (struct sockaddr *)&client, (int*)&ret);
sprintf(warning_str, "Remote Address %s, port %d\n",
inet_ntoa(client.sin_addr), ntohs(client.sin_port) );
warns( __LINE__, warning_str );
if ((pid = fork()) == -1) {
fatal(__LINE__, "fork");
}
if (pid == 0) {
HCST hCst;
char *ptr_src, *ptr_prefix, *ptr_suffix, *ptr_std, *ptr_trkno;
char *p1, *p2, *p3;
char *request, *reply;
int request_len;
long reply_len;
short tag, status;
int i, rc, count = 0;
close( s );
// locate the memory.
if( NULL == (ptr_src = malloc(ETHER_LEN)) ) {
fatal(__LINE__, "malloc");
}
memset(ptr_src, 0, ETHER_LEN);
if( NULL == (ptr_prefix = malloc(ETHER_LEN)) ) {
fatal(__LINE__, "malloc");
}
memset(ptr_prefix, 0, ETHER_LEN);
if( NULL == (ptr_suffix = malloc(TRKNO_LEN)) ) {
fatal(__LINE__, "malloc");
}
memset(ptr_suffix, 0, TRKNO_LEN);
if( NULL == (ptr_trkno = malloc(TRKNO_LEN)) ) {
fatal(__LINE__, "malloc");
}
memset(ptr_trkno, 0, TRKNO_LEN);
if( NULL == (ptr_std = malloc(ETHER_LEN+TRKNO_LEN)) ) {
fatal(__LINE__, "malloc");
}
memset(ptr_std, 0, ETHER_LEN+TRKNO_LEN);
if( NULL == (request = malloc(MAX_MSG_SIZE)) ) {
fatal(__LINE__, "malloc");
}
memset(request, 0, MAX_MSG_SIZE);
if( NULL == (reply = malloc(MAX_MSG_SIZE)) ) {
fatal(__LINE__, "malloc");
}
memset(reply, 0, MAX_MSG_SIZE);
reply_len = MAX_MSG_SIZE;
/*
* create xml client/server transporter connection.
*/
hCst = CreateCST( server_port );
if( !hCst ) {
//GetCSTItemInt(hCst, CI_ERROR, &rc);
//sprintf( warning_str, "rc=", rc );
//warns( __LINE__, warning_str );
fatal(__LINE__, "CSTCreate Error");
}
SetCSTItemInt(hCst, CI_TIMEOUT, 30);
SetCSTItemInt(hCst, CI_XLATE_MODE, CST_ANY_SERVER);
if ( OpenCST(hCst) < 0 ) {
// inform client to quit, then close self.
memset(reply, 0, MAX_MSG_SIZE);
strcpy(reply, TERMINATE_RESPONSE_1);
//reply[strlen("TERMINATE")] = 0;
send(ns, reply, strlen(reply), 0);
cstperror(hCst, "CSTOpen Error");
close( ns );
DestroyCST( hCst );
fatal(__LINE__, "CSTOpen Error");
}
time( &tv1 );
while (1) {
AGAIN_LOOP:
/*
* clear up needed memory.
*/
memset(ptr_src, 0, ETHER_LEN);
memset(ptr_prefix, 0, ETHER_LEN);
memset(ptr_std, 0, ETHER_LEN+TRKNO_LEN);
/*
* copy ptr_suffix to ptr_std firstly.
*/
if (ptr_suffix) {
strcpy(ptr_std, ptr_suffix);
}
memset(ptr_suffix, 0, TRKNO_LEN);
/*
* receive message from client. (need signal_alarm?)
*
* (1) EWOULDBLOCK
* Nonblocking mode has been set on the socket, and the read
* operation would block.
* (Normally, recv blocks until there is input available to be read.)
* (2) EINTR
* The operation was interrupted by a signal before any data was read
*/
ret = recv(ns, ptr_src, ETHER_LEN, 0);
if( ret < 0 ) {
if (errno == EINTR) {
continue;
}
// The socket is marked blocking and the requested operation block.
else if (errno == EWOULDBLOCK) {
sprintf(warning_str, "recv: EAGAIN=EWOULDBLOCK ", errno);
warns( __LINE__, warning_str );
}
else {
sprintf( warning_str, "recv(non-EINTR): ", errno );
warns( __LINE__, warning_str);
}
}
else if( ret == 0 ) { // End-of-this-Loop
sprintf( warning_str,"recv: tcp read stack is null, close process");
warns( __LINE__, warning_str );
goto SUMUP;
}
// warns(__LINE__, ptr_src);
/*
* cut the tail(,12) into ptr_suffix.
*/
p1 = strrchr(ptr_src, ',');
//if( *(p1+1) != NULL ) {
if( *(p1+1) ) {
strcpy(ptr_suffix, p1+1);
//warns(__LINE__, ptr_suffix);
}
/*
* copy str_src except tail(,12) into ptr_prefix.
*/
p2 = ptr_src + strlen(ptr_src) - 1;
for( i=0; i<strlen(ptr_src); i++ )
if( *(p2-i) == ',')
break;
strncpy(ptr_prefix, ptr_src, strlen(ptr_src)-i);
//warns(__LINE__, ptr_prefix);
strncat( ptr_std, ptr_prefix, strlen(ptr_prefix) );
// warns(__LINE__, ptr_std);
p3 = ptr_std; i=0;
while( *p3 ) {
/*
* Firstly, I must deal with illegal input.
* 1. continue comma: ,,,,
* 2. space: 123, 456, 789,912,
*/
i = get_trkno(p3, ptr_trkno);
//sprintf(warning_str, "ptr_trkno is ", ptr_trkno);
sprintf(warning_str, "",ptr_trkno);
warns(__LINE__, warning_str);
//warns(__LINE__, ptr_trkno);
// 1. continue comma: if(*ptr_trkno == NULL)
if( ptr_trkno == NULL ) {
warns(__LINE__, "tracking number is null");
p3 += i;
continue;
}
/*
* 2. space:
* client send trackingno like ", 01234 ,12 34," will automatically
* change to '01234,1234,', so both available.
*
ret = get_space( ptr_trkno );
if(ret < 0) {
sprintf(warning_str, "trackno has space", ptr_trkno);
warns(__LINE__, warning_str);
p3 += i;
continue;
}
*/
p3 += i;
/* The implementation:
* account1: entity1, entity2,...
* account2: entity1, entity2,...
* continue till the last account' last entity.
* AGAIN_REQUEST is seperated from different entity,
* TERMINATE_REQUEST interrupt the whole loop.
*/
if(0 == memcmp(ptr_trkno, AGAIN_REQUEST, 10)) {
memset(ptr_suffix, 0, TRKNO_LEN);
memset(reply, 0, MAX_MSG_SIZE);
strcpy(reply, AGAIN_RESPONSE);
reply[strlen(AGAIN_RESPONSE)] = 0;
if (send(ns, reply, strlen(reply), 0) == -1) {
fatal(__LINE__, "send");
}
time( &tv2 );
sprintf(warning_str,"Total tracking#, seconds.",
count, tv2-tv1);
warns( __LINE__, warning_str );
time( &tv1 );
goto AGAIN_LOOP;
}
/* to prevent from EWOULDBLOCK
* Nonblocking mode has been set on the socket, and the read
* operation would block. (Normally, recv blocks until there
* is input available to be read.)
*/
if(0 == memcmp(ptr_trkno, TERMINATE_REQUEST, strlen(ptr_trkno))) {
/*
* p4="\n";
* send "\n" is very important, then 'TERMINATE' string.
* if not, the client recv core buffer doesn't flush, the input
* was added to the end of xml data.
* char *p4="\n";
*
* send(ns, p4, strlen(p4), 0);
*/
memset(reply, 0, MAX_MSG_SIZE);
strcpy(reply, TERMINATE_RESPONSE);
reply[strlen("TERMINATE")] = 0;
if (send(ns, reply, strlen(reply), 0) == -1) {
fatal(__LINE__, "send");
}
goto SUMUP;
}
memset(request, 0, MAX_MSG_SIZE);
memset(reply, 0, MAX_MSG_SIZE);
sprintf( request, "%s%s%s", xml_format_start_2, ptr_trkno, xml_format_end_2);
//warns( __LINE__, request );
request_len = strlen( request );
rc = WriteCST(hCst, request, request_len, 0, 0);
if( rc != CST_OK ) {
GetCSTItemInt(hCst, CI_ERROR, &rc);
sprintf( warning_str, "WriteCSTEx: ", rc );
if( rc == CST_TIMEOUT )
continue;
}
rc = ReadCSTEx(hCst, reply, reply_len, &tag, &status);
if(rc <= 0) {
sprintf( warning_str, "ReadCSTEx: RC=%d, TAG=%d, STATUS=%d", rc, tag, status);
warns( __LINE__, warning_str );
if (rc == 0) {
warns( __LINE__, "connection closed");
break;
}
GetCSTItemInt(hCst, CI_ERROR, &rc);
sprintf( warning_str, "ReadCSTEx: ", rc );
warns( __LINE__, warning_str );
continue;
if (rc == CST_DATA_PENDING) {
warns( __LINE__, "CST_DATA_PENDING occured\n");
continue;
}
}
else {
// warns(__LINE__, reply);
}
/*
* (1) EPIPE(SIGPIPE)
* This socket was connected but the connection is now broken.
* In this case, send generates a SIGPIPE signal first; if that
* signal is ignored or blocked, or if its handler returns, then
* send fails with EPIPE.
* (2) EINTR
* The operation was interrupted by a signal before any data was sent
* (3) EWOULDBLOCK (should never happened)
* Nonblocking mode has been set on the socket, and the write
* operation would block.
* (Normally send blocks until the operation can be completed.)
*/
ret = send(ns, reply, strlen(reply), 0);
if( ret == -1 ){
if (errno == EINTR) {
continue;
}
else if (errno = EPIPE) {
/*
* when client dis-connect, the server's send return EPIPE error.
* in this case, the server connection closes positively.
*/
warns( __LINE__, "send: EPIPE" );
/*
* peer closed, 'Broken pipe' generated
* on this side if still sending.
*/
goto SUMUP;
}
else if (errno == EWOULDBLOCK) {
sprintf( warning_str, "send: EAGAIN=EWOULDBLOCK ", errno);
warns( __LINE__, warning_str );
}
else {
sprintf( warning_str, "send: ", errno) ;
warns( __LINE__, warning_str );
}
}
else if (ret == 0) {
goto SUMUP;
}
++ count;
} //while( *p3 )
} //while (1)
SUMUP:
time( &tv2 );
sprintf(warning_str,"Total tracking#, seconds.",
count, tv2-tv1);
warns( __LINE__, warning_str );
free (ptr_src);
free (ptr_prefix);
free (ptr_suffix);
free (ptr_trkno);
free (ptr_std);
free (request);
free (reply);
CloseCST( hCst );
DestroyCST( hCst );
close(ns);
exit(-1);
} //pid==0
}
close(s);
}
/* CSV:
* 123,456,789,
*/
int get_trkno(char* left_trknos, char *trkno)
{
int i;
for(i=0; i<strlen(left_trknos); i++)
if( *(left_trknos+i) == ',' ) {
strncpy(trkno, left_trknos, i);
*(trkno+i) = '';
left_trknos += i;
break;
}
return ++i;
}
/*
* use ps -axj
*/
int daemon_init (void)
{
pid_t pid;
if ((pid = fork()) 0 ) {
sprintf( warning_str, "Child %d terminated.", pid );
warns( __LINE__, warning_str );
}
}
int cleanup (int sock, HCST hCst)
{
} //end cleanup
// shutdown is important for daemon tcp server.
// william copy from example.
int shutdown_close(int how_many, int socket1, int socket2)
{
int retval;
/*
* shut down and close socket completely.
*/
retval = shutdown(socket1, 2);
if (retval == -1) {
fatal(__LINE__, "shutdown");
}
retval = close (socket1);
if (retval) {
fatal(__LINE__, "close");
}
/*
* If given, shutdown and cliose socket2)
*/
if (how_many == 2) {
retval = shutdown(socket1, 2);
if (retval == -1) {
fatal(__LINE__, "shutdown");
}
retval = close (socket1);
if (retval) {
fatal(__LINE__, "close");
}
}
exit(1);
}
/*
* ANSI C POSIX, SVR4, BSD are different about process.
*
* By stevens SIGCHLD process. but failure.
static void sig_cld(void)
{
pid_t pid;
int status;
warns(__LINE__, "SIGCLD received\n");
if( signal(SIGCLD, sig_cld) == -1 )
warns( __LINE__, "signal error");
if( (pid = wait(&status)) < 0)
warns( __LINE__, "wait error");
sprintf( warning_str, "pid = %d\n", pid );
warns( __LINE__, warning_str );
return;
}
*/
/*
* justify if there is space(s) in the tracking number.
*/
int get_space(char *track_no)
{
int i;
for (i = 0; i < strlen(track_no); i ++)
if( isspace(*(track_no+i)) )
return -1;
return 1;
}
void get_socket_params( int s )
{
int size=0, bufsize = sizeof(int);
/*
* SO_RCVBUF: hold incoming data from a particular socket.
* it is used as the advertised windows (how much data the perer can accept
* for socket before transmission must stop and await clearance to proceed).
* SO_SNDBUF: hild data that is to be transmitted.
* Most stack implementations default the sizes of the socket buffers to
* 8192 bytes.
*/
getsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&size, &bufsize);
sprintf( warning_str, "socket receive buf size:\t%d", size);
warns( __LINE__, warning_str );
getsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&size, &bufsize);
sprintf( warning_str, "socket send buf size:\t%d", size);
warns( __LINE__, warning_str );
/*
* just for select?
* SO_RCVLOWAT: specifies the low-water mark for the receive buffer.
* (default 1)
* SO_SNDLOWAT: specifies the low-water mark for the send buffer.
* (default 2048)
*/
getsockopt(s, SOL_SOCKET, SO_RCVLOWAT, (void *)&size, &bufsize);
sprintf( warning_str, "Low-water mark for receive buf:\t%d", size);
warns( __LINE__, warning_str );
getsockopt(s, SOL_SOCKET, SO_SNDLOWAT, (void *)&size, &bufsize);
sprintf( warning_str, "Low-water mark for send buf:\t%d", size);
warns( __LINE__, warning_str );
// get current time-to-live value set in IP packets:
// failure on Solaris 5.6
getsockopt(s, IPPROTO_IP, IP_TTL, (void *)&size, &bufsize);
sprintf( warning_str, "IP Packet TTL:\t %d", size);
warns( __LINE__, warning_str );
/*
* TCP_MAXSEG: manipulate the Maximum Segment Size (MSS).
* The Maximum amount of data that TCP will send to the peer in
* a singel datagram. MSS=MTU-40.
*/
getsockopt(s, IPPROTO_TCP, TCP_MAXSEG, (void *)&size, &bufsize);
sprintf( warning_str, "Maximum Segment Size:\t%d", size);
warns( __LINE__, warning_str );
exit(1);
}
void get_help(void) {
fprintf(stderr, "\nUsage: xmlcst_agent -h -a -s \n\
Description: \n\
-h: this help message. \n\
-a: get the server socket parameters \n\
-s: server & port of XML Server, default:fxvip27.prod.dummy.com:9101\n\\n");
exit (1);
}
Like this:
Like Loading...
Nice Blog with Excellent information