William Jiang

JavaScript,PHP,Node,Perl,LAMP Web Developer – http://williamjxj.com; https://github.com/williamjxj?tab=repositories

xml_tcp.c

#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);
}

One response to “xml_tcp.c

  1. love 08/03/2011 at 6:44 pm

    Nice Blog with Excellent information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 73 other followers

%d bloggers like this: