您当前的位置: 首页 > 

phymat.nico

暂无认证

  • 2浏览

    0关注

    1967博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

select模型详解

phymat.nico 发布时间:2017-05-21 16:42:04 ,浏览量:2

1.select模型原理

使用select函数检查文件描述符上是否有io事件发生,包括可读,可写以及异常

 select参数和返回值意义如下:

int select (

 IN int nfds,                           //0,无意义

 IN OUT fd_set* readfds,      //检查可读性

 IN OUT fd_set* writefds,     //检查可写性

 IN OUT fd_set* exceptfds,  //例外数据

 IN const struct timeval* timeout);    //函数的返回时间

 参数说明:

第一个参数nfds在linux表示要监视的最大文件描述符+1,在windows下为0

第二个参数readfds检查文件描述符集合可读

第三个参数writefds检查文件描述符集合可写

第四个参数exceptfds检查文件描述符集合异常

第五个参数timeout结构如下

struct  timeval {

        long    tv_sec;        //秒

        long    tv_usec;     //毫秒

};

设置select函数返回的等待时间

如果参数timeout设为:

NULL,则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件。

0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

返回值:

执行成功则返回文件描述符状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,没有返回;当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。错误值可能为: EBADF 文件描述词为无效的或该文件已关闭 EINTR 此调用被信号所中断 EINVAL 参数n 为负值。 ENOMEM 核心内存不足

select返回fd_set中可用的套接字个数。 系统调用:

 fd_set是一个SOCKET集合(数组),以下宏可以对该集合进行操作:

FD_CLR( s, *set) 从集合set删除句柄s;

FD_ISSET( s, *set) 检查句柄s是否存在与集合set中;

FD_SET( s, *set )把句柄s添加到集合set中;

FD_ZERO( *set ) 把set队列初始化集合成空.

2.select工作流程

1:用FD_ZERO宏来初始化我们感兴趣的fd_set。

也就是select函数的第二三四个参数。

2:用FD_SET宏来将套接字句柄分配给相应的fd_set。

如果想要检查一个套接字是否有数据需要接收,可以用FD_SET宏把套接接字句柄加入可读性检查集合中

3:调用select函数。

如果该套接字没有数据需要接收,select函数会把该套接字从可读性检查集合中删除掉,

4:用FD_ISSET对套接字句柄进行检查。

如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么说明马上可以进行相应的IO操 作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的fd_set里,那么说明当前数据已经来了, 马上可以读取成功而不会被阻塞。

3.使用实例

下面给出一个基于udp组播在windows下的实现,linux下可能略有不同

#include 
#include 
#include 
#include 
#include 
#include 

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "mswsock.lib")
#define MAX_FD 16
fd_set fdread;
SOCKET fd_arr[MAX_FD] = {0};
unsigned int __stdcall threadfunc(void *)
{
	while (1)
	{
		fd_set fd_tmp = fdread;
		timeval tmv;
		tmv.tv_sec = 1;
		tmv.tv_usec = 0;
		int ret = select(0, &fd_tmp, NULL, NULL, &tmv);
		if (ret < 0)
		{
			printf("select failed:%d\n",ret);
			break;
		}
		else
		{
			for (unsigned int i=0;i 0)
				{
					SOCKADDR_IN client_addr;
					int nlen_addr = sizeof(SOCKADDR_IN);
					char buff[40960]={0};
					int nrecv = recvfrom(fd, buff, 40960, 0, (SOCKADDR *)&client_addr, &nlen_addr);
					if (nrecv < 0)
					{
						printf("recvfrom error:%d\n", WSAGetLastError());
						continue;
					}
					printf("sock:%d src addr:%s:%d recv data:%s\n", 
						fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port),buff);
				}
			}
			/*for (int i=0;i            
关注
打赏
1659628745
查看更多评论
0.0444s