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

/* 创建一个tcp socket */
int sw_tcp_socket()
{
	return socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
}

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

/* 连接远端 */
int sw_tcp_connect(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 connect(skt, (struct sockaddr *)&addr, sizeof(addr));
}

/* 绑定接收地址和端口, sever使用bind()与本机绑定, client使用connect()与远程主机连接 */
int sw_tcp_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));
}

/* 开始监听, max为处理连接的队列长度, 建议设置为: SOMAXCONN */
int sw_tcp_listen(int skt, int max)
{
	return listen(skt, max);
}

/* 等待远程连接(从连接队列中读取第一个已完成的连接, 队列长度减一) */
int sw_tcp_accept(int skt, unsigned int *ip, unsigned short *port)
{
	struct sockaddr_in from;
	socklen_t slen = sizeof(from);
	
	skt = accept(skt, (struct sockaddr *)&from, &slen);
	if(0 <= skt)
	{
		if(ip)   *ip   = from.sin_addr.s_addr;
		if(port) *port = from.sin_port;
	}
	
	return skt;
}

/* 发送数据 */
int sw_tcp_send(int skt, const char *buf, int send_len)
{
	return send(skt, buf, send_len, 0);
}

/* 接收数据 */
int sw_tcp_recv(int skt, char *buf, int buf_size)
{
	return recv(skt, buf, buf_size, 0);
}

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

/* 检测tcp socket的状态, timeout单位为ms */
int sw_tcp_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);
}
