/************************************************************************
 * AUTHOR: NiuJiuRu
 * FILENAME: swudp.c
 * DESCRIPTION: UDP socket
 * NOTE:
 * HISTORY:
 * 1, [2010-11-30] created by NiuJiuRu
 ***********************************************************************/
#include "swapi.h"
#include "swudp.h"

/* 创建一个udp socket */
int sw_udp_socket()
{
	return socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
}

/* 关闭一个udp socket */
void sw_udp_close(int skt)
{
	closesocket(skt);
}

/* 绑定接收地址和端口 */
int sw_udp_bind(int skt, unsigned int ip, unsigned short port)
{
	struct sockaddr_in addr;
	
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = ip;
	addr.sin_port = port;
	
	return bind(skt, (struct sockaddr *)&addr, sizeof(addr));
}

/* 加入一个多播组 */
int sw_udp_join(int skt, unsigned int ip)
{
	int ttl = 127;
	bool fFlag = true;
	struct ip_mreq mreq;
	
	if(IS_MULTICAST_IP(ip))
	{
		mreq.imr_multiaddr.s_addr = ip;
		mreq.imr_interface.s_addr = INADDR_ANY;
 		
		if(setsockopt(skt, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == SOCKET_ERROR)
		{
			sw_log_error("add to one multicast group error!!");
			return -1;
		}
		
		if(setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) == SOCKET_ERROR)
		{
			sw_log_warn("set multicast ttl args fail!");
		}
		
		if(setsockopt(skt, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&fFlag, sizeof(fFlag)) == SOCKET_ERROR)
		{
			sw_log_warn("set multicast loopback args fail!");
		}
	}
	
	return 0;
}

/* 退出一个多播组 */
int sw_udp_drop(int skt, unsigned int ip)
{
	struct ip_mreq mreq;
	
	if(IS_MULTICAST_IP(ip))
	{
		mreq.imr_multiaddr.s_addr = ip;
		mreq.imr_interface.s_addr = INADDR_ANY;
		return setsockopt(skt, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq));
	}
	
	return  0;
}

/* 发送数据 */
int sw_udp_send(int skt, unsigned int ip, unsigned short port, char *buf, int send_len)
{
	struct sockaddr_in to;
	unsigned int len = sizeof(to);
	
	to.sin_family = AF_INET;
	to.sin_addr.s_addr = ip;
	to.sin_port=  port;
	
	return sendto(skt, buf, send_len, 0, (struct sockaddr *)&to, len);
}

/* 接收数据 */
int sw_udp_recv(int skt, unsigned int *ip, unsigned short *port, char *buf, int buf_size)
{
	struct sockaddr_in from;
	unsigned int len = sizeof(from);
	
	len = recvfrom(skt, buf, buf_size, 0, (struct sockaddr *)&from, &len);
	if(ip) *ip = from.sin_addr.s_addr;
	if(port) *port = from.sin_port;
	
	return len;
}

/* 配置udp socket */
int sw_udp_ioctl(int skt, int type, unsigned long *val)
{
	return ioctlsocket(skt, (long)type, val);
}

/* 检测udp socket的状态, timeout单位为ms */
int sw_udp_select(int skt, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int timeout)
{
	struct timeval tv = { 0 };
	
	if(readfds)
	{
		FD_ZERO(readfds);
		FD_SET((unsigned int)skt, readfds);
	}
	
	if(writefds)
	{
		FD_ZERO(writefds);
		FD_SET((unsigned int)skt, writefds);
	}
	
	if(exceptfds)
	{
		FD_ZERO(exceptfds);
		FD_SET((unsigned int)skt, exceptfds);
	}
	
	if(0 <= timeout)
	{
		tv.tv_sec = timeout/1000;
		tv.tv_usec = (timeout%1000)*1000;
		return select(skt+1, readfds, writefds, exceptfds, &tv);
	}
	else
		return select(skt+1, readfds, writefds, exceptfds, NULL);
}
