使用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
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?