C语言中select函数简介及使用

select函数用来检查套接字描述符(sockets descriptors)是否已准备好读/,提供了一种同时检查多个套接字的方法。

Linux中select函数的声明在/usr/include/x86_64-linux-gnu/sys/select.h文件中,Windows下select函数的声明在WinSock2.h文件中,声明如下:

// Linux
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
// Windows
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout);
// macros
FD_SET(int fd, fd_set *set); // Add fd to the set
FD_CLR(int fd, fd_set *set); // Remove fd from the set
FD_ISSET(int fd, fd_set *set); // Return true if fd is in the set
FD_ZERO(fd_set *set); // Clear all entries from the set

不像socket中connect、accept、recv这几个函数属于阻塞方式,而select函数属于非阻塞方式。在使用select函数时,会经常用到四个宏FD_SET(将一个指定的文件描述符加入集合)、FD_CLR(将一个指定的文件描述符从集合中删除)、FD_ISSET(检查一个指定的文件描述符是否在集合中)、FD_ZERO(清空集合)。类型fd_set存放的是一组文件描述符的集合,在Linux系统中,如设备、管道、FIFO等都可通过文件描述符的形式来访问。文件描述符在形式上是一个非负整数,实际上,它是一个索引值。套接字也是文件描述符。

select函数参数介绍:

第一个参数nfds在Linux指的是highest-numbered的文件描述符+1,类型为int。在Windows下,这个参数可以忽略,可以是任意值。

第二个参数readfds是可选的,若不为null,select返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

第三个参数writefds是可选的,若不为null,select返回一个大于0的值,表示有文件可写;如果没有可写的文件,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

第四个参数exceptfds是可选的,若不为null,select返回一个大于0的值,表示有异常发生在文件集合中;如果没有异常发生,则根据timeout参数的值再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

第五个参数timeout是可选的,若不为null,则用来设置超时时间,则为等待的超时时间;若为null,则将select设置为阻塞状态,直到文件描述符集合中某个文件描述符发生变化时才会返回结果。

返回值:返回-1表示调用select函数时有错误发生,具体的错误在Linux可通过errno输出来查看,在Windows下可通过WSAGetLastError查看;返回0,表示select函数超时;返回正数即调用select函数成功,表示集合中文件描述符的数量,集合也会被修改以显示哪一个文件描述符已准备就绪。

以下为测试代码(funset_socket.cpp):

#include "funset.hpp"
#ifdef _MSC_VER
#include <WinSock2.h>
#include <winsock.h>
#else
#include <sys/select.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#include <iostream>

int test_select_1()
{
#ifdef _MSC_VER
	fd_set fds;
	FD_ZERO(&fds);

	timeval tv;
	tv.tv_sec = 2;
	tv.tv_usec = 0;

	int ret = select(0, &fds, nullptr, nullptr, &tv);
	if (ret == SOCKET_ERROR) {
		fprintf(stderr, "fail to select, error: %d\n", WSAGetLastError());
		return -1;
	} else if (ret == 0) {
		fprintf(stderr, "select timeout\n");
		return -1;
	} else {
		fprintf(stdout, "success to select\n");
	}
#else
	const char* path = "/dev/video0";
	int fd = open(path, O_RDWR);
	if (fd == -1) {
		fprintf(stderr, "fail to open device: %s\n", path);
	}

	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	struct timeval tv;
	tv.tv_sec = 2;
	tv.tv_usec = 0;

	int ret = select(fd+1, &fds, nullptr, nullptr, &tv);
	if (ret == -1) {
		fprintf(stderr, "fail to select, error: %d, %s\n", errno, strerror(errno));
		return -1;
	} else if (ret == 0) {
		fprintf(stderr, "select timeout\n");
		return -1;
	} else {
		fprintf(stdout, "success to select\n");
	}

	close(fd);
#endif

	return 0;
}

在Linux下执行结果如下:

GitHubhttps://github.com/fengbingchun/OpenSSL_Test

  • 19
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值