#include "sock_utils.h"


/*	Create TCP client socket.
	Parameters: pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
	Return: success--socket handle; else -1
*/
int CreateTCPClientSock()
{
	//struct sockaddr_in sa_cli;
	int nRet = -1;
	//int err = 0;
	
	nRet = socket(AF_INET, SOCK_STREAM, 0);
	if (nRet == -1 )
		return -1;

	return nRet;
}


/*	Create UDP client socket.
	Parameters: pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
	Return: success--socket handle; else -1
*/
int CreateUDPClientSock()
{
	//struct sockaddr_in sa_cli;
	int nRet = -1;

	nRet = socket(AF_INET, SOCK_DGRAM, 0);
	if (nRet == -1 )
		return -1;
	return nRet;
}

/*	Connect to server with server IP, port
	Parameters: pServerIP	-- Server IP address, can not be NULL
				nServerPort -- Server port
				sd			-- Socket
	Return: success--0; else -1
*/
int ClientConnect(int sd, const char * pServerIP, unsigned short nServerPort)
{	
	struct sockaddr_in sa_serv;
	int err = 0;

	memset (&sa_serv, '\0', sizeof(sa_serv));
	sa_serv.sin_family      = AF_INET;
	sa_serv.sin_addr.s_addr = inet_addr(pServerIP);
	sa_serv.sin_port        = htons (nServerPort);          

	err = connect( sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv) );
	if (err == -1)
	{
		return -1;
	}
	return 0;
}


#if 0
/* 	Create TCP socket of server, call socket(), bind(), listen() 
	Parameters: pIP			-- The IP server want to bind, if pIP==NULL, bind INADDR_ANY
				nPort		-- The port server want to bind
				nMaxConnect -- the Max connect the server can accept, use by listen()
	Return: success--socket handle; else -1, can get last error by errno */
int CreateTCPSrvSock( const char *  pIP, unsigned short nPort, int nMaxConnect )
{
	struct sockaddr_in sa_serv;
	int nRet = -1;
	int err = 0;

#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(0x202, &wsaData) != 0) 
	{
		return -1;
	}
#endif

	nRet = socket(AF_INET, SOCK_STREAM, 0);   
	if (nRet == -1 )
		return -1;

	memset (&sa_serv, '\0', sizeof(sa_serv));
	sa_serv.sin_family      = AF_INET;
	if( ( pIP != NULL )&& ( pIP[0] != 0 ))
		sa_serv.sin_addr.s_addr = inet_addr(pIP);
	else
		sa_serv.sin_addr.s_addr = INADDR_ANY;
	sa_serv.sin_port        = htons (nPort);          /* Server Port number */

	err = bind(nRet, (struct sockaddr*) &sa_serv, sizeof (sa_serv)); 
	if (err == -1)
	{
		CloseSocket( nRet );
		return -1;
	}
		     
	err = listen (nRet, nMaxConnect );
	if (err == -1)
	{
		CloseSocket( nRet );
		return -1;
	}

	return nRet;
}

/* 	Create UDP socket of server, call socket(), bind()
	Parameters: pIP			-- The IP server want to bind, if pIP==NULL, bind INADDR_ANY
				nPort		-- The port server want to bind
	Return: success--socket handle; else -1, can get last error by errno */
int CreateUDPSrvSock( const char *  pIP, unsigned short nPort )
{
	struct sockaddr_in sa_serv;
	int nRet = -1;
	int err = 0;

#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(0x202, &wsaData) != 0) 
	{
		return -1;
	}
#endif

	nRet = socket(AF_INET, SOCK_DGRAM, 0);   
	if (nRet == -1 )
		return -1;

	memset (&sa_serv, '\0', sizeof(sa_serv));
	sa_serv.sin_family      = AF_INET;
	if( ( pIP != NULL )&& ( pIP[0] != 0 ))
		sa_serv.sin_addr.s_addr = inet_addr(pIP);
	else
		sa_serv.sin_addr.s_addr = INADDR_ANY;
	sa_serv.sin_port        = htons (nPort);          /* Server Port number */

	err = bind(nRet, (struct sockaddr*) &sa_serv, sizeof (sa_serv)); 
	if (err == -1)
	{
		CloseSocket( nRet );
		return -1;
	}
		     
	return nRet;
}

/* Accept connect from client, format client IP to string, call accept()
	Parameters: szClientIP	--	If accept suceess, return client IP in this buffer, format: xxx.xxx.xxx.xxx
				pPort		--	If accept suceess, return client port
	Return: success--new socket handle; else -1 
*/
int SockAccept( int Socket, char szClientIP[20], unsigned short *pPort)
{
	struct sockaddr_in sa_cli;
	size_t client_len;
	unsigned char * p = NULL;
	int sd;

	client_len = sizeof(sa_cli);
	sd = accept (Socket, (struct sockaddr*) &sa_cli, &client_len);
	if (sd == -1)
		return -1;

	p = (unsigned char *)(&(sa_cli.sin_addr.s_addr));
	sprintf(szClientIP, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3));
	*pPort = ntohs( sa_cli.sin_port );

	return sd;
}

/* 
Parameters: timeout--0 wait forever, >0 timeout limit( second )
Return: Socket handle is ready -- >=1; timeout -- 0; socket error -- <0 */
int SockSelect( int sockfd, int timeout )
{
	fd_set          rfds;
	struct timeval  tv;
	
	FD_ZERO( &rfds );
	FD_SET( sockfd, &rfds );
	if( timeout > 0 )
	{
		tv.tv_sec = timeout;
		tv.tv_usec = 0;
		return select( sockfd + 1, &rfds, NULL, NULL, &tv );
	}
	else
		return select( sockfd + 1, &rfds, NULL, NULL, NULL );
}

/*	Create TCP client socket.
	Parameters: pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
	Return: success--socket handle; else -1
*/
int CreateTCPClientSock( const char *  pLocalIP )
{
	struct sockaddr_in sa_cli;
	int nRet = -1;
	int err = 0;

#ifdef WIN32
	WSADATA wsaData;
	if (WSAStartup(0x202, &wsaData) != 0)
	{
		return -1;
	}
#endif

	nRet = socket(AF_INET, SOCK_STREAM, 0);
	if (nRet == -1 )
		return -1;

	if( ( pLocalIP != NULL )&& ( pLocalIP[0] != 0 ))
	{
		memset (&sa_cli, '\0', sizeof(sa_cli));
		sa_cli.sin_family      = AF_INET;
		sa_cli.sin_addr.s_addr = inet_addr(pLocalIP);
		sa_cli.sin_port        = htons (0);          /* Server Port number */
		err = bind(nRet, (struct sockaddr*) &sa_cli, sizeof (sa_cli));
		if (err == -1)
		{
			CloseSocket( nRet );
			return -1;
		}
	}

	return nRet;
}

/*	Create UDP client socket.
	Parameters: pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
	Return: success--socket handle; else -1
*/
int CreateUDPClientSock( const char *  pLocalIP )
{
	struct sockaddr_in sa_cli;
	int nRet = -1;
	int err = 0;

	nRet = socket(AF_INET, SOCK_DGRAM, 0);
	if (nRet == -1 )
		return -1;

	if( ( pLocalIP != NULL )&& ( pLocalIP[0] != 0 ))
	{
		memset (&sa_cli, '\0', sizeof(sa_cli));
		sa_cli.sin_family      = AF_INET;
		sa_cli.sin_addr.s_addr = inet_addr(pLocalIP);
		sa_cli.sin_port        = htons (0);          /* Server Port number */
		err = bind(nRet, (struct sockaddr*) &sa_cli, sizeof (sa_cli));
		if (err == -1)
		{
			CloseSocket( nRet );
			return -1;
		}
	}

	return nRet;
}


/*	Connect to server with server IP, port
	Parameters: pServerIP	-- Server IP address, can not be NULL
				nServerPort -- Server port
				sd			-- Socket
	Return: success--0; else -1
*/
int ClientConnect( const char * pServerIP, unsigned short nServerPort, int sd )
{	
	struct sockaddr_in sa_serv;
	int err = 0;

	memset (&sa_serv, '\0', sizeof(sa_serv));
	sa_serv.sin_family      = AF_INET;
	sa_serv.sin_addr.s_addr = inet_addr(pServerIP);
	sa_serv.sin_port        = htons (nServerPort);          /* Server Port number */

	err = connect( sd, (struct sockaddr*)&sa_serv, sizeof(sa_serv) );
	if (err == -1)
	{
		return -1;
	}
	return 0;
}

/*	Connect to server with server IP, port in time
	Parameters: pServerIP	-- Server IP address, can not be NULL
				nServerPort -- Server port
				sd			-- Socket handle
				nTimeout	-- Timeout(second)
	Return: success--socket handle; else -1
*/
int ClientConnect_Intime( const char * pServerIP, unsigned short nServerPort, 
						   int sd, int nTimeout )
{
	int ret;

#ifndef WIN32
	signal_handle_t OldFuncPtr = signal(SIGALRM, key_timeout);
#endif
	_ALARM(nTimeout);
	if( ( SET_JMP(key_env) ) != 0)
	{
		_ALARM( 0 );
#ifndef WIN32
		signal( SIGALRM, OldFuncPtr );
#endif
		return -2;//out time
	}
	ret = ClientConnect( pServerIP, nServerPort, sd );
	_ALARM(0);
#ifndef WIN32
	signal( SIGALRM, OldFuncPtr );
#endif

	return ret;
}


/*	Connect to server with server IP, port
	Parameters: pServerIP	-- Server IP address, can not be NULL
				nServerPort -- Server port
				pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
	Return: success--socket handle; else -1
*/
int ConnectTo( const char * pServerIP, unsigned short nServerPort, const char *  pLocalIP )
{	
	int nRet = -1;

	nRet = CreateTCPClientSock( pLocalIP );
	if( nRet < 0 ) return nRet;
	
	if( ClientConnect( pServerIP, nServerPort, nRet ) != 0 )
	{
		CloseSocket( nRet );	
		return -1;
	}
	return nRet;
}

/*	Connect to server with server IP, port in time
	Parameters: pServerIP	-- Server IP address, can not be NULL
				nServerPort -- Server port
				pLocalIP	-- Local IP address, if you don't bind local IP, set it as NULL
				nTimeout	-- Timeout(second)
	Return: success--socket handle; else -1
*/
int ConnectTo_Intime( const char * pServerIP, unsigned short nServerPort, 
					const char *  pLocalIP,int nTimeout )
{
	int nRet = -1;

	if( ClientConnect_Intime( pServerIP, nServerPort, nRet, nTimeout ) != 0 )
	{
		CloseSocket( nRet );	
		return -1;
	}
	return nRet;
}

/* Receive fixed length data from socket
 like as recv but no pipe broken
 * */
int SockRecv(int sd, void *buff, int len)
{
	return recv(sd, buff, len, MSG_NOSIGNAL);
}

/* Receive fixed length data from socket
 * Parameters: timeout -- 0 wait forever, >0 timeout limit( second )
 * return : OK >=1; timeout -- 0; socket error -- <0
 * */
int SockRecvFixLen(int sd, void *buff, int fix_len, int timeout)
{
	int len = 0, sum = 0;
	int ret;
	
	while( sum < fix_len ){
		ret = SockSelect(sd, timeout);
		if( ret <= 0 )
			return ret;
		len = recv(sd, buff+sum, fix_len - sum, MSG_NOSIGNAL);
		if(len < 0)
			return len;
		if(len == 0) //Peer closed
			return -1;
		sum += len;
	};
	
	return sum;
}

int SockSendFixLen(int sock, void *buff, int len)
{
	int offset = 0;
	while(len){
		int c = send(sock, buff+offset, len, MSG_NOSIGNAL);
		//2009-07-04, 由于发现send在网线断后不返回，测试
		//int c = send(sock, buff+offset, len, 0);
		//if(c <= 0)
		//	printf("send return bad result: %d\n", c);
		if(c < 0)
			return c;
	
		len -= c;
		offset += c;
	}

	return offset;
}
#endif
