///////////////////////////////////////////////////////////////////////////////////
//交易报文组成:交易报文头+交易报文体
//交易报文头:ASC码0x01 + 报文头长度(3bytes,右对齐不足左补零)+
//	     是否携带文件标志('0'-不带文件, '1'-携带文件) + 文件名长度('00'--'99') + 
//	     文件名称 + 交易报文体长度('00000001'-'99999999') + 
//	     携带文件长度('0000000000'-'9999999999')
//交易报文体:由用户提供报文
//文件报文:文件报文体
//文件报文体:所携带文件的ASC码
///////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "tcppubfun.h"

# ifndef   INADDR_NONE
	# define   INADDR_NONE	0xffffffff
# endif

extern int errno;
u_short gd_tcpportbase = 0;
int gd_tcpworksvrchildpidarr[256];
int gd_tcpworksvrchildpidarrdim = 0;
char	gd_LogFile[256];


int connectsock(const char *host, const char *service, const char *transport);
int passiveTCP(const char *service, int qlen);
int passivesock(const char *service, const char *transport, int qlen);
void reaper(int sig);
int memcat(char *sr, int slen, char *dest, int pos);
int  writesock(int sock, char *hdata, int hsize);
int ReadWait(int sockfds, int blocktime);
int start_tcp_serv(char *service, int qlen, int (*fun)(int sock), 
        int (*fconndb)(), int (*fdisconndb)(), int  pnum);
void onquitsvrchild(int sig);
int TcpInWorkChild(int msock, int (*fun)(int sock));

//tcpdtime--取得当前时间和日期
//Argument:
//      asctm--当前时间, hhmmss
//      ascday--当前日期, yyyymmdd
int tcpdtime(char * asctm,char * ascday)
{

	time_t 	t;
	struct tm 	* tm1;

	t = time(NULL);
	tm1 = localtime(&t);
	
        sprintf(ascday,"%04d",tm1->tm_year+1900);
        sprintf(ascday+4,"%02d",tm1->tm_mon+1);
        sprintf(ascday+6,"%02d",tm1->tm_mday);
        
        sprintf(asctm,"%02d",tm1->tm_hour);
        sprintf(asctm+2,"%02d",tm1->tm_min);
        sprintf(asctm+4,"%02d",tm1->tm_sec);

	return 0;
}


//Function:与服务端应用建立连接.
//      return  0--Normal
//              <0--Fail
//Argument:
//	host --服务方的主机名称或IP地址
//      service--服务方应用的端口号
int connectTCP(const char *host, const char *service)
{
	int	ret;
	
	ret = connectsock(host, service, "tcp");
	
	return ret;
}

//Function:allocate & connect a socket using TCP or UDP		
//	-1--service参数错误
//	-2--host参数错误
//	-3--transport--参数错误 Not "tcp" or "udp"
//	-4--socket()函数调用失败
//	-5--connect()函数调用失败
//Arguments:
//	host --服务方的主机名称或IP地址
//      service--服务方应用的端口号
//	transport--name of transport protocol to use("tcp" or "udp")
int connectsock(const char *host, const char *service, const char *transport)
{
	struct hostent *phe, *phe1;	/*pointer to host information entry*/
	struct servent *pse;	/*pointer to service informattion entry*/
	struct protoent *ppe;	/*pointer to protocol information entry*/
	struct sockaddr_in sin, client_addr; /*an internet endpoint address	*/
	int s, type;		/*socket descriptor and socket type	*/
	u_long baddr;
	int	ret, len, buflen;
	struct sockaddr *pSkAddr;

	sprintf(gd_LogFile, "%s/%s", getenv("TCPLOGDIR"), TCPLOGFILE);

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	
	//Map service name to port number
	if(pse = getservbyname(service, transport))
		sin.sin_port = pse->s_port;
	else if((sin.sin_port = htons((u_short)atoi(service))) == 0)
	{
		Tcpvwf(gd_LogFile, "In connectsock(), errno[%d]\n", errno);
		return(-1);
	}

	//Map host name to IP address, allowing for dotted decimal
	if(phe = gethostbyname(host))
	{
		memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
	}
	else
	{		
		baddr = inet_addr(host) ;
		if(baddr == INADDR_NONE)
		{
			Tcpvwf(gd_LogFile, "In connectsock gethostbyname inet_addr, errno[%d]\n", errno);
			return(-2);
		}
		else
			memcpy(&sin.sin_addr, &baddr, 4); 
	}
	
	//Map transport protocol name to protocol number
	if((ppe = getprotobyname(transport)) == 0)
	{
		Tcpvwf(gd_LogFile, "In connectsock getprotobyname, errno[%d]\n", errno);
		return(-3);
	}

	//Use protocol to choose a socket type
	if(strcmp(transport, "udp") == 0)
		type = SOCK_DGRAM;
	else
		type = SOCK_STREAM;

	//Allocate a socket
	s = socket(PF_INET, type, ppe->p_proto);
	if(s < 0)	
	{
		Tcpvwf(gd_LogFile, "In connectsock socket, errno[%d]\n", errno);
		return(-4);
	}

	//len = sizeof(int);
	//buflen = 1024*60;
	//ret = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buflen, len);
	//if (ret < 0)
	//{
	//	Tcpvwf(LogFile, "in setsockopt errno[%d]\n", errno);
	//	return(-1);
	//}
	//Tcpvwf(LogFile, "in setsockopt buflen[%d]\n", buflen);

	//Connect the socket
	pSkAddr = (struct sockaddr *)&sin;
	if(connect(s, pSkAddr, sizeof(sin))<0)
	{
		Tcpvwf(gd_LogFile, "In connectsock connect, errno[%d]\n", errno);
		close(s);
		return(-5);
	}

	return(s);
}	 


//Function:启动服务端TCP应用	
//Argument:
//      service--服务端应用使用的端口号
//	qlen--允许在排队使用套接字的客户端进程数
int passiveTCP(const char *service, int qlen)
{
	return passivesock(service, "tcp", qlen);
}

//Function:启动服务端应用
//	-1--service参数错误
//	-2--transport--参数错误
//	-3--socket()函数调用失败
//	-4--bind()函数调用失败
//	-5--listen()函数调用失败
//Arguments:
//      service--服务端应用使用的端口号
//	transport--transport protocol to use("tcp" or "udp")
//	qlen--允许在排队使用套接字的客户端进程数
int passivesock(const char *service, const char *transport, int qlen)
{
	int len, ret, type, sock ;	/*socket descriptor and socket type	*/
	struct servent *pse;	/*pointer to service informattion entry*/
	struct protoent *ppe;	/*pointer to protocol information entry*/
	struct sockaddr_in sin; 	/*an internet endpoint address	*/
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	struct  linger  optlinger;
	
	//Map service name to port number
	if(pse = getservbyname(service, transport))
		sin.sin_port = htons(ntohs((u_short)pse->s_port) + gd_tcpportbase);
	else if((sin.sin_port = htons((u_short)atoi(service))) == 0)
	{
		Tcpvwf(gd_LogFile, "in passivesock, errno[%d]\n", errno);
		return(-1);
	}

	//Map transport protocol name to protocol number
	ppe = getprotobyname(transport);
	if(ppe == 0)
	{
		Tcpvwf(gd_LogFile, "in passivesock getprotobyname, errno[%d]\n", errno);
		return(-2);
	}

	//Use protocol to choose a socket type
	if(strcmp(transport, "udp") == 0)
		type = SOCK_DGRAM;
	else
		type = SOCK_STREAM;

	//Allocate a socket
	sock = socket(PF_INET, type, ppe->p_proto);
	if(sock < 0)
	{
		Tcpvwf(gd_LogFile, "in passivesock socket, errno[%d]\n", errno);
		return(-3);
	}

	//struct  linger  optlinger;
	//optlinger.l_onoff = 1;
	//optlinger.l_linger = 0;
	//len = sizeof(struct linger);
	//ret = setsockopt(sock, SOL_SOCKET, SO_LINGER, &optlinger, len);
	//if (ret < 0)
	//{
	//	Tcpvwf(gd_LogFile, "in setsockopt SO_LINGER errno[%d]\n", errno);
	//	return(-1);
	//}

	//Bind the socket
	if(bind(sock, (struct sockaddr *)&sin, sizeof(sin))<0)
	{
		Tcpvwf(gd_LogFile, "in passivesock bind, errno[%d]\n", errno);
		close(sock);
		return(-4);
	}

	if((type == SOCK_STREAM) && (listen(sock, qlen) < 0))
	{
		Tcpvwf(gd_LogFile, "in passivesock listen, errno[%d]\n", errno);
		close(sock);
		return(-5);
	}

	return(sock);
}	 


//Function:清理退出的子进程占用的系统资源
//Arguments:
//sig--信号SIGCHLD
void reaper(int sig)
{
	int status, i;

	while(wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
	{
		i ++;
	}
	tcp_signal(SIGCHLD, reaper);
}


//交易报文头:ASC码0x01 + 报文头长度(3bytes,右对齐不足左补零)+
//	     是否携带文件标志('0'-不带文件, '1'-携带文件) + 文件名长度('00'--'99') + 
//	     文件名称 + 交易报文体长度('00000001'-'99999999')
//readtxnmsghead--读交易报文头
//Argument:
//	withfileflag--是否携带文件标志, 0-不带文件, 1-携带文件
//	filename--携带的文件名称
//	msglen--交易报文体长度
int readtxnmsghead(int sock, int *withfileflag, char *filename, int *msglen, int *filesize)
{
	int  	i, j, rsize, ret;
	int  	n;		/*n-返回字节数  count-'read'执行次数*/
	int  	pos;	
	char 	buf[512], head[512], str[128];

	//交易报文头当前位置指针
	pos = 0;
	//读交易报文头第一个字节,以确定报文种类
	n = read(sock, buf, 1);
	if (n < 0)	//超时,或网络异常
	{
		Tcpvwf(gd_LogFile, "In readtxnmsghead() read, ret[%d] errno[%d]\n", n, errno);
		return -1;
	}
	//发来FIN信号
	if (n == 0)  return -1;

	//head缓冲区用于存放报文头
	head[pos] = buf[0];
	pos ++;

	i = 0;
	if (buf[0] != 0x01)  //交易报文
	{
		Tcpvwf(gd_LogFile, "In readtxnmsghead(), message type error buf[0]=[%d]\n", buf[0]);
		return -1;
	}
	//读取报文头总长度
	n = read(sock, buf, 3);
	if (n <= 0)	//超时,或网络异常或发来FIN信号
	{
		Tcpvwf(gd_LogFile, "In readtxnmsghead read, ret[%d] errno[%d]\n", n, errno);
		return -1;
	}
	memcpy(head+pos, buf, n);
	pos += n;
	j = 3-n;
	if (j > 0)
	{
		for (i=0; i msgbodylen)
			rsize = msgbodylen-pos;
		else
			rsize = k;
		n = read(sock, buf, rsize);

		if (n <= 0)	//超时,或网络异常, 发来FIN信号
		{
			Tcpvwf(gd_LogFile, "In readtxnmsgbody() read, ret[%d] errno[%d]\n", n, errno);
			return -2;
		}
		memcat(buf, n, msgbody, pos);
		pos += n;		

		//已读完报文体
		if (pos == msgbodylen) 	break;
	}

	return 0;
}


//readtxnmsgfile--读交易报文携带文件
//Argument:
//	sock--
//	msgfilename--携带的文件名
//	msgfilesize--携带文件大小
int readtxnmsgfile(int sock, char *msgfilename, int msgfilesize)
{
	char 	path[512], fullfn[640], buf[1024];
	int	fd, pos, ret, n, i, j, k, c, rsize, pid, pid1;

	strcpy(path, getenv("PRINTDIR"));
	umask(0);
	if (strlen(path) == 0)
		sprintf(fullfn, "%s", msgfilename);
	else
		sprintf(fullfn, "%s/%s", path, msgfilename);
	
	fd = creat(fullfn, 0644);
	if (fd < 0)
	{
		Tcpvwf(gd_LogFile, "In readtxnmsgfile() creat, errno[%d]\n", errno);
		return -1;
	}	
	k = sizeof(buf);
	//i=可将报文分成若干块
	//i = msgfilesize / sizeof(buf);
	//j=将报文分成若干块余数字节
	//j = msgfilesize % sizeof(buf);
	//c=报文总长-余数
	//c = msgfilesize - j;
	pos = 0;
	for (;;)
	{
		if (pos+k > msgfilesize)
			rsize = msgfilesize-pos;
		else
			rsize = k;
		n = read(sock, buf, rsize);
		if (n <= 0)	//超时,或网络异常, 发来FIN信号
		{
			Tcpvwf(gd_LogFile, "In readtxnmsgfile read, ret[%d] errno[%d]\n", n, errno);
			close(fd);
			return -1;
		}
		ret = write(fd, buf, n);
		if (ret != n)
		{
			Tcpvwf(gd_LogFile, "In readtxnmsgfile writefile, ret[%d] n[%d] errno[%d]\n", ret, n, errno);
			close(fd);
			return -1;
		}
		pos += n;
		if (pos == msgfilesize)  break;
	}
	close(fd);

	return 0;
}


//交易报文头:ASC码0x01 + 报文头长度(3bytes,右对齐不足左补零)+
//	     是否携带文件标志('0'-不带文件, '1'-携带文件) + 文件名长度('00'--'99') + 
//	     文件名称 + 交易报文体长度('00000001'-'99999999') + 
//	     携带文件长度('0000000000'-'9999999999')
//genmsghead-生成报文头
//Argument:
//	srbufsize--报文主体长度
//	msgfilename--交易报文后携带的文件, 若无msgfilename为空串
//	msgbufhead--生成的交易报文头
//	msgfilesize--交易携带文件大小
int  genmsghead(int srbufsize, char *msgfilename, char *msgbufhead, int *msgfilesize)
{
	int	pos, slen, fd, ret;
	char 	path[512], fullfn[640], buf[1024];
	struct stat fbuf;
	
	pos = 0;
	//交易报文开始标志0x01
	msgbufhead[0] = 0x01;
	
	pos ++;
	slen = strlen(msgfilename);
	//传输的文件文件名不准超过99个字符
	if (slen > 99)
	{
		Tcpvwf(gd_LogFile, "In genmsghead filename is too long, len[%d]\n", slen);
		return -2;					
	}
	
	//报文头长度
	sprintf(msgbufhead+pos, "%03d", 25+slen);

	//是否携带文件标志
	pos += 3;
	if (slen == 0)
		msgbufhead[pos] = '0';
	else
		msgbufhead[pos] = '1';

	//文件名长度
	pos += 1;
	sprintf(msgbufhead+pos, "%02d", slen);
	
	//文件名称
	pos += 2;
	if (slen > 0)
		sprintf(msgbufhead+pos, "%s", msgfilename);
	
	//被发送报文体长度在1-99999999之间
	if ((srbufsize < 1) || (srbufsize > 99999999))
	{
		Tcpvwf(gd_LogFile, "In genmsghead source buf is too large, [%d]\n", srbufsize);
		return -2;					
	}
	
	//报文体长度
	pos += slen;
	sprintf(msgbufhead+pos, "%08d", srbufsize);	

	//默认携带文件长度为0
	*msgfilesize = 0;

	//携带文件长度
	pos += 8;
	if (slen == 0)
		sprintf(msgbufhead+pos, "%010d", 0);
	else
	{
		strcpy(path, getenv("PRINTDIR"));
		
		if (strlen(path) == 0)
			sprintf(fullfn, "%s", msgfilename);
		else
			sprintf(fullfn, "%s/%s", path, msgfilename);
		memset(&fbuf, 0, sizeof(struct stat));
		ret = stat(fullfn, &fbuf);
		if (ret != 0)
		{
			Tcpvwf(gd_LogFile, "In genmsghead stat, errno[%d]\n", errno);
			return -1;
		}
		sprintf(msgbufhead+pos, "%010d", fbuf.st_size);
		*msgfilesize = fbuf.st_size;
	}
	pos += 10;
	msgbufhead[pos] = 0;

	return 0;
}

//wrmsgfilebysock--写报文中携带文件
//Argument:
//	msgfilename--报文中携带文件名
//	msgfilesize--报文中携带文件长度
int  wrmsgfilebysock(int sock, char *msgfilename, int msgfilesize)
{
	char 	path[512], fullfn[640], buf[10240];
	int	fd, pos, n, ret, pid, pid1, i;
	
	memset(path, 0, sizeof(path));
	strcpy(path, getenv("PRINTDIR"));
	
	if (strlen(path) == 0)
		sprintf(fullfn, "%s", msgfilename);
	else
		sprintf(fullfn, "%s/%s", path, msgfilename);
	
	fd = open(fullfn, O_RDONLY);
	if (fd < 0)
	{
		Tcpvwf(gd_LogFile, "In wrmsgfilebysock open, errno[%d]\n", errno);
		return -1;
	}

	pos = 0;
	i = 0;
	for (;;)
	{
		n = read(fd, buf, sizeof(buf));
		i ++;
		if (n < 0)  
		{	
			close(fd);
			Tcpvwf(gd_LogFile, "In wrmsgfilebysock 被传送文件在exception now[%d] ori[%d]\n", pos, msgfilesize);
			return -1;
		}

		if (n == 0)  
		{	
			if (pos != msgfilesize)
			{
				close(fd);
				Tcpvwf(gd_LogFile, "In wrmsgfilebysock 被传送文件在exception now[%d] ori[%d]\n", pos, msgfilesize);
				return -1;
			}
			else
				return 0;
		}

		pos += n;
		if (pos > msgfilesize)
		{
			close(fd);
			Tcpvwf(gd_LogFile, "In wrmsgfilebysock 被传送文件在增长 now[%d] ori[%d]\n", pos, msgfilesize);
			return -1;
		}
		ret = writesock(sock, buf, n);
		if (ret < 0)
		{
			close(fd);
			Tcpvwf(gd_LogFile, "In wrmsgfilebysock writesock errno[%d]\n", errno);
			return -1;
		}
		if (pos == msgfilesize)  break;
	}
	close(fd);

	return 0;
}


//getpeerip:取得对方的IP地址和断口号  
//      return  0--Normal
//              <0--Fail
//Argument:
//	sock--插口
//	PeerIP--对方IP
//	PeerPort--对方端口号
int getpeerip(int sock, char *PeerIP, char *PeerPort)
{
	int  i,ret;
	struct sockaddr  addr;
	int	addrlen;
	unsigned char	ch;
	char	tmp[4];
	struct sockaddr_in sin;

	addrlen = sizeof(struct sockaddr);
	ret = getpeername(sock, (struct sockaddr *)&addr, &addrlen);
	if (ret < 0)	//超时,或网络异常
	{
		Tcpvwf(gd_LogFile, "In sever getpeername, ret[%d] errno[%d]\n", ret, errno);
		return -1;
	}

	memcpy(&sin, (struct sockaddr_in *)&addr, sizeof(struct sockaddr_in));
	
	sprintf(PeerPort, "%d", sin.sin_port);

	PeerIP[0] = 0;
	for (i=0; i<4; i++)
	{
		ch = ((unsigned char *)(addr.sa_data))[i+2];
		if (i < 3)
			sprintf(tmp, "%d.", ch);
		else
			sprintf(tmp, "%d", ch);
		strcat(PeerIP, tmp);
	}

	return 0;
}

		
//Function:内存连接
//Argument:
//	sr--源指针
//	slen--源数据长度
//	dest--目标数据指针
//	pos--源数据连接到目标数据的开始位置
int memcat(char *sr, int slen, char *dest, int pos)
{
	int i, j;

	memcpy(dest+pos, sr, slen);

	return(0);
}

//Tcpvwf--记日志工具
//Argument:
//      fn--被追加的日志文件名
//      args--变参形式
int Tcpvwf(char *fn, char *args, ...)
{
        va_list ap;
        char    *arr[128];
        int argno = 0;
	FILE *fp;

	fp = fopen(fn, "a");

        va_start(ap, args);	
        vfprintf(fp, args, ap);
	fclose(fp);
	va_end(ap);
	
	return 0;
}


//cli_write--客户端发交易请求
//Argument:
//	sock--
//	hdata--客户端请求报文
//	hsize--请求报文长度,可包含空字符
//	hfile--携带的文件
int  cli_write(int sock, char *hdata, int hsize, char *hfile)
{
	int 	ret, withsfileflag, fd, n;
	int 	hfilesize, sfilesize;
	char	msgbufhead[256], buf[1024];
	
	//生成交易报文头msgbufhead
	ret = genmsghead(hsize, hfile, msgbufhead, &hfilesize);
	if (ret < 0)
	{
		close(sock);
		return(-1);
	}
	//写报文头
	ret = writesock(sock, msgbufhead, strlen(msgbufhead));
	if (ret < 0)
	{
		close(sock);
		return(-1);
	}
	//写报文主体
	ret = writesock(sock, hdata, hsize);
	if (ret < 0)
	{
		close(sock);
		return(-1);
	}
	
	//若有携带文件
	if (hfilesize > 0)
	{
		ret = wrmsgfilebysock(sock, hfile, hfilesize);
		if (ret < 0)
		{
			close(sock);
			return(-1);
		}
	}

	return(0);
}

//Function:客户端向服务器发送交易,并接收回应结果
//	 0--Normal
//	 <0--Fail
//Arguments:
//	sock--插口
//	hdata--被发送的数据
//	hsize--被发送的数据长度
//      hfile--发送携带的文件,strlen(hfile)==0表示不携带文件; 必须为该参数分配空间
//      sdata--接收的服务端的回应报文
//      ssize--回应报文长度
//      sfile--若服务端有文件返回,它表示文件名称;strlen(file)==0表示不携带文件;
//                                      必须为该参数分配足够空间
int  client_request(char *host, char *sevice, char *hdata, int hsize, char *hfile, 
			char *sdata, int *ssize, char *sfile)
{
	int     ret, withsfileflag;
	int     sockfd, hfilesize, sfilesize;
	char	msgbufhead[256];

        //与服务端建立连接
	sockfd = connectTCP(host, sevice);
	if(sockfd < 0)	return(-1);
	
	//生成交易报文头msgbufhead
	ret = genmsghead(hsize, hfile, msgbufhead, &hfilesize);
	if (ret < 0)
	{
		close(sockfd);
		return(-1);
	}
	//写报文头
	ret = writesock(sockfd, msgbufhead, strlen(msgbufhead));
	if (ret < 0)
	{
		close(sockfd);
		return(-1);
	}
	//写报文主体
	ret = writesock(sockfd, hdata, hsize);
	if (ret < 0)
	{
		close(sockfd);
		return(-1);
	}
	
	//若有携带文件
	if (hfilesize > 0)
	{
	        //发送携带文件
		ret = wrmsgfilebysock(sockfd, hfile, hfilesize);
		if (ret < 0)
		{
			close(sockfd);
			return(-1);
		}
	}

        //以下为读取服务端回应数据过程
        
        //读取报文头
	ret = readtxnmsghead(sockfd, &withsfileflag, sfile, ssize, &sfilesize);
	if (ret < 0)	
	{
		close(sockfd);
		return -1;
	}
	//读交易报文体
	ret = readtxnmsgbody(sockfd, *ssize, sdata);
	if (ret < 0)	
	{
		close(sockfd);
		return -1;
	}
	//回应数据中若携带文件,读取携带文件
	if (withsfileflag == 1)
	{
		ret = readtxnmsgfile(sockfd, sfile, sfilesize);
		if (ret < 0)	
		{
			close(sockfd);
			return -1;
		}
	}
	else
		sfile[0] = 0;

	close(sockfd);

	return(0);
}

//Function:接收客户端发送来的数据
//	 0--Normal
//	 -1--客户端发来断链请求
//Arguments:
//	sock--插口
//	hdata--接收到数据的存放地址
//	hsize--接收到数据长度
int serv_read(int sock, char *hdata, int *hsize, char *hfile)
{
	int	ret, withhfileflag, hfilesize;

	ret = readtxnmsghead(sock, &withhfileflag, hfile, hsize, &hfilesize);
	if (ret < 0)	return -1;
	ret = readtxnmsgbody(sock, *hsize, hdata);
	if (ret < 0)	return -1;

	if (withhfileflag == 1)
	{
		ret = readtxnmsgfile(sock, hfile, hfilesize);
		if (ret < 0)	return -1;
	}
	else
		hfile[0] = 0;

	return(0);
}

//Function:发送回应数据给客户端
//	 0--Normal
//       <0--Fail
//Arguments:
//	sock--插口
//	sdata--被发送数据
//	ssize--数据长度
int serv_write(int sock, char *sdata, int ssize, char *sfile)
{
	int	ret, sfilesize;
	char	msgbufhead[256];

	//生成交易报文头msgbufhead
	ret = genmsghead(ssize, sfile, msgbufhead, &sfilesize);
	if (ret < 0)	return(-1);

	//写报文头
	ret = writesock(sock, msgbufhead, strlen(msgbufhead));
	if (ret < 0)	return(-1);

	//写报文主体
	ret = writesock(sock, sdata, ssize);
	if (ret < 0)	return(-1);
	
	//若有携带文件
	if (sfilesize > 0)
	{
		ret = wrmsgfilebysock(sock, sfile, sfilesize);
		if (ret < 0)	return(-1);
	}

	return(0);
}


//服务端应用SIGTERM信号的处理
void onquitsvrchild(int sig)
{
	int	i, ret;
	
	for (i=0; i 0)	i ++;
		if (i == gd_tcpworksvrchildpidarrdim)	break;
	}
	//父进程结束
	exit(0);
}

//start_tcp_serv:起动面向连接的TCP服务器
//	1-- fail passivesock
//	2--accept函数调用失败
//Arguments:
//	service--服务端口号
//	qlen--等待队列长度
//	fun--Server 收到请求时所执行的函数, 不可为NULL
//      fconndb--外部应用程序提供的连接数据库函数, 可为NULL
//      fdisconndb--外部应用程序提供的断开数据库函数, 可为NULL
//	pnum--服务端进程数
//注:实参中fun, fconndb, fdisconndb三个函数不能重名
int start_tcp_serv(char *service, int qlen, int (*fun)(int sock), 
        int (*fconndb)(), int (*fdisconndb)(), int  pnum)
{
	int msock, ssock, hsize;
	int n, i, j, ret, pid, pid1;
	int  alen;
	struct sockaddr_in fsin;
	fd_set	infds;
	int	maxfd;
	char	asctm[7], ascday[9];

	memset(gd_tcpworksvrchildpidarr, 0, sizeof(gd_tcpworksvrchildpidarr));
	gd_tcpworksvrchildpidarrdim = pnum;

	tcp_signal(SIGINT, SIG_IGN);
	tcp_signal(SIGHUP, SIG_IGN);
	//父进程收到终止信号后,结束所有服务端进程
	tcp_signal(SIGTERM, onquitsvrchild);

	sprintf(gd_LogFile, "%s/%s", getenv("TCPLOGDIR"), TCPLOGFILE);

        //初始化服务端插口
	msock = passivesock(service, "tcp", qlen);
	if(msock < 0)	return(1);

        //启动若干服务端子进程	
	for (i=0; i 0)
		{
			gd_tcpworksvrchildpidarr[i] = pid;
			printf("Child process %d is started.\n", pid);
		}
		else
		{
			Tcpvwf(gd_LogFile, "in start_tcp_serv(), fork() errno[%d] \n", errno);
			return 1; //父进程收到退出码1,也不再重新创建进程
		}
	}
	
	//父进程监控子进程状态,若子进程终止,父进程再重新创建一个子进程.
	printf("Parent process %d is started.\n", pid);
	i = 0;
	for (;;)
	{
		ret = 0;
		pid = wait(&ret);
		//pid<0表示wait()操作被信号中断,这时再执行wait()操作,便会返回
		//终止子进程进程号
		if (pid < 0) continue;
		ret >>= 8;
		if (ret < 9)	//应用退出码>=9,系统错退出码1--8
		{
			tcpdtime(asctm, ascday);
			Tcpvwf(gd_LogFile, "pid %d is died exit[%d] time[%s]\n", pid, ret, asctm);
		}
                if (ret == 3)  //若子进程连接数据库失败,应用将终止.
                {
			tcpdtime(asctm, ascday);
			Tcpvwf(gd_LogFile, "pid %d is died for conn db fail time[%s]\n", pid, asctm);
			//终止应用
			onquitsvrchild(SIGTERM);
                }                        
		pid1 = fork();
		if (pid1 == 0)
		{
                        //连接数据库函数若不为NULL,则执行连接数据库操作.
                        if (fconndb != NULL)
                        {
                                ret = fconndb();
                                //若连接数据库失败,则子进程退出码约定为3
                                if (ret != 0)   exit(3);
                        }
			ret = TcpInWorkChild(msock, fun);
			//若断开数据库函数不为空,则断开数据库
			if (fdisconndb != NULL)  fdisconndb();
			exit(ret);
		}
		else if (pid1 < 0)
		{
			Tcpvwf(gd_LogFile, "in start_tcp_serv(), fork() errno[%d] \n", errno);
			return 1; /*父进程收到退出码1,也不再重新创建进程*/
		}
		else
		{
		        //更新父进程中进程号数组
			for (i=0;i0--Normal
//Arguments:
//	sock--插口
//	hdata--宿主机数据流
//	hsize--宿主机数据流长度
int  writesock(int sock, char *hdata, int hsize)
{
	int ret;

	//Write to sock
	ret = write(sock, hdata, hsize);
	if(ret != hsize)
	{
		Tcpvwf(gd_LogFile, "In writesock(), ret != hsize ret[%d] hsize[%d] errno[%d]\n", ret, hsize, errno);
		return -1;
	}

	return(0);
}

//ReadWait--检测插口描述符是否已经读就绪
//      0--Ready
//      <0--Not Ready
//Argument:
//      sockfds--插口描述符
//      blocktime--若当时未就绪,检测过程等待的时间.
int ReadWait(int sockfds, int blocktime)
{
	int n,i,tmp,maxsock = 0;
	fd_set rdfd;
        int readysocknum = 0;
        struct timeval abc;
	struct timeval timeout;

        timeout.tv_sec = blocktime/1000;
        timeout.tv_usec = blocktime%1000;

        FD_ZERO(&rdfd);
	FD_SET(sockfds, &rdfd);
	maxsock = sockfds;

	if (select(maxsock+1, &rdfd, (fd_set *)0, (fd_set *)0, &timeout) <0)
		return -1;

	if (FD_ISSET(sockfds, &rdfd)!=0)  
        	return 0;
        else
        	return -1;
}


int TcpRandNum()
{
	int	i, iseed;
	char	sec[20], tmp[8];
	
	TcpMilSecond(sec);
	memcpy(tmp, sec+12, 7);
	tmp[7] = 0;
	iseed = atol(tmp) + getpid();
	srand(iseed);
	i = rand();
	i /= 100;

	return i;	
}

//TcpSleep一阻塞方式睡眠,可被超时信号中断
//stime--睡眠时间(单位:毫秒)
int TcpSleep(long stime)
{
	struct timeval tv;
	int tmp;
	
	if (stime <= 0)
		return 0;
	tmp = stime%1000;
	tv.tv_sec = stime / 1000;
	tv.tv_usec = tmp*1000;
	tmp = select(0,NULL,NULL,NULL,&tv);
	if (tmp < 0)	return -1;
	
	return 0;
}


int TcpMilSecond(char *sec)
{
        struct timeval  first;
        struct timezone tzp;
        int	ret;

        ret = gettimeofday (&first, &tzp);
 
	sprintf(sec, "%012d%07d", first.tv_sec, first.tv_usec);

	return 0;
}

     
void *tcp_signal(int signo, void (*func)(int))
{
	struct sigaction  act, oact;
	
	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (signo == SIGALRM)
	{
#ifdef SA_INTERUPT
		act.sa_flags |= SA_INTERUPT;	//SunOS
#endif
	}
	else
	{
#ifdef SA_RESTART
		act.sa_flags |= SA_RESTART;	//SVR4, 4.3+BSD
#endif		
	}
	if (sigaction(signo, &act, &oact) < 0)
		return (NULL);

	return (void *)(oact.sa_handler);
}