select 函数
1、用途
在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在这里,无法执行下面的代码。这是就需要用到非阻塞的编程方式,使用select函数就可以实现非阻塞编程。
select函数是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。
2、原理
select需要驱动程序的支持,驱动程序实现fops内的poll函数。select通过每个设备文件对应的poll函数提供的信息判断当前是否有资源可用(如可读或写),如果有的话则返回可用资源的文件描述符个数,没有的话则睡眠,等待有资源变为可用时再被唤醒继续执行。
3、协议定义
int select(int nfds, fd_set* readset, fd_set* writeset, fe_set* exceptset, struct timeval* timeout);
参数:
nfds 需要检查的文件描述字个数
readset 用来检查可读性的一组文件描述字。
writeset 用来检查可写性的一组文件描述字。
exceptset 用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)
timeout 超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间
返回值:
返回fd的总数,错误时返回SOCKET_ERROR
4、
void http_tick(void)
{
int max_fd = -1; // 最大socket 数目加1 用于 selsect 函数
int cli_addr_len; //客户端地址
int sock_fd = -1;
int new_conn_fd = -1;
fd_set read_set;
fd_set select_read_set;
struct sockaddr_t serv_addr;
struct sockaddr_t cli_addr;
struct timeval_t t, timeout;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
t.tv_sec = 2;
t.tv_usec = 0;
FD_ZERO(&read_set);
FD_ZERO(&select_read_set);
for (uint8 i = 0; i < MAX_CLIENT; i++)
{
clientfd[i] = -1;
}
if (sock_fd==-1)
{
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
uart_Debug("Fail to socket");
}
}
unsigned int value = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&value, sizeof(value)) < 0)
{
uart_Debug("Fail to setsockopt");
}
memset(&serv_addr, 0, sizeof(serv_addr));
memset(&cli_addr, 0, sizeof(cli_addr));
serv_addr.s_port = 80;
if (bind(sock_fd, &serv_addr, sizeof(serv_addr))< 0)
{
uart_Debug("Fail to bind");
}
if (listen(sock_fd, 0) < 0)
{
uart_Debug("Fail to listen");
}
max_fd = sock_fd;
FD_SET(sock_fd, &read_set);
for(;;)
{
max_fd = sock_fd;
for (uint8 i = 0; i < MAX_CLIENT; i++)
{
if (max_fd < clientfd[i])
{
max_fd = clientfd[i];
}
}
select_read_set = read_set;
uint8 ret = select(max_fd + 1, &select_read_set, NULL, NULL, &timeout);
if (ret == 0)
{
// uart_Debug("timeout\n");
}
else if (ret < 0)
{
uart_Debug("error occur\r\n");
}
else
{
if (FD_ISSET(sock_fd, &select_read_set))
{
// uart_Debug("new client comes\r\n");
cli_addr_len = sizeof(cli_addr);
new_conn_fd =accept(sock_fd, (struct sockaddr_t*)&cli_addr, &cli_addr_len);
if (new_conn_fd < 0)
{
uart_Debug("Fail to accept\r\n");
}
else
{
for(uint8 i=0; i<MAX_CLIENT; i++)
{
if ( clientfd[i] == -1)
{
clientfd[i] = new_conn_fd;
FD_SET(new_conn_fd, &read_set);
// setsockopt(new_conn_fd, 0, SO_SNDTIMEO, &timeout, sizeof(struct timeval_t));
break;
}
if (max_fd < new_conn_fd)
{
max_fd = new_conn_fd;
}
}
}
}
else
{
for (uint8 i = 0; i < MAX_CLIENT; i++)
{
if (-1 == clientfd[i])
{
continue;
}
if (FD_ISSET(clientfd[i], &select_read_set))
{
// uart_Debug("FD_ISSET success 01 \r\n");
/*rev 接收数据 NumOfBytes = recv(index, httpRequest, HTTP_DATA_MAX_LEN, 0);*/
msleep(500);
close(clientfd[i]);
clientfd[i] = -1;
}
}
}
}
StartHttpTask_Heartbeat = 0;
mico_thread_msleep(10);
}
}