Linux中socket的select()函数使用

socket通讯分为两种模式:阻塞和非阻塞模式。

阻塞模式在读写缓冲区时都是lock的,即没有退出或没有获得预期结果时会一直卡住不返回。对于read操作,缓冲区没有数据时会一直等待并将当前线程挂起,知道缓冲区有数据过来再唤起线程继续执行,对于write操作,当缓冲区没有足够空间写入数据时,也会一直等待,直到缓冲区有足够的空间写入数据再返回结果继续执行下面的代码。

非阻塞模式就是和阻塞模式相对的了,非阻塞模式不会卡住函数和线程,执行后会立即返回结果,如果是错误会返回-1,并设置响应的errno。

socket默认是阻塞模式,设置非阻塞可以在创建socket时传入参数,也可以在后面修改。

创建非阻塞socket

socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, 0);//SOCK_NONBLOCK为非阻塞标识

设置非阻塞模式的方法

//方法一:fcntl
int flag = fcntl(fd, F_GETFL, 0);
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);
	
//方法二:ioctl 
int b_on = 1;
ioctl (fd, FIONBIO, &b_on);

在一些socket的操作中,阻塞的socket可能会造成一系列的问题,如服务器的连接等,如果大量用户去做连接操作可能会导致较长的延迟,因此,可能需要使用非阻塞的socket来保证操作不会阻塞线程。

在非阻塞socket中,可以使用select()函数来判断缓冲区是否可用,从而反馈结果,程序判断结果决定是否继续读写数据或做其他的事情。

select()函数:

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); 

参数:

1.maxfdp:一个整数值,指集合中所有文件描述符的范围,通常是文件描述符的最大值加1。

2.readfds:一个fd_set结构的指针,集合中包括需要判断的一个或多个文件描述符,如果这个集合中有一个文件描述符可以读取数据,select函数就会返回一个大于0的值,表示有数据可读,如果没有可读的数据,则会根据timeout参数判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。当readfds传入NULL或0时,表示select不关心任何文件描述符的读取变化。
3.writefds:同样是一个指向fd_set结构的指针,与上述readfds作用相似,只不过writefds关心的是数据的写入,readfds关心的是数据的读取。
4.errorfds:和上面两个参数的意图相似,用来监视文件错误异常,如不关心异常可传入NULL或0。
5.timeout:是select的超时时间,类型是struct timeval结构体,timeval用来代表时间值,有两个成员,一个是秒数,另一个是毫秒数。timeout设置NULL时,即不传入时间结构,会select置于阻塞状态,一直等到监视文件描述符集合中某个文件描述符状态发生变化为止;timeout若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值。当timeout设置一个非0的时间时,select会在timeout时间内阻塞,当监测到状态发生变化会立即返回,否则则会等超时时间到达后立即返回。

函数的返回值:

负值时:select发生错误

正值时:集合中某个或某些文件描述符状态为可操作(错误集合暂时不考虑)

0时:等待超时,没有可读写或错误的文件

使用情况,如下代码,检测描述符下缓冲区是否有数据可读:

int SockCanRead(int sock)
{
	fd_set readset;
	FD_ZERO(&readset);
	FD_SET(sock, &readset);
	timeval tout;
	tout.tv_sec = 0;
	tout.tv_usec = 0;
	
	int ret = 0;
	if (( ret = select((sock+1), &readset, 0, 0, &tout)) > 0)
	{
		if ( FD_ISSET(sock, &readset) )
			ret = 1;
		else
			ret = 0;
	}
	return ret;
}
上述函数中使用select判断了文件描述符对应的文件或缓冲区是否有数据可以读取,其中创建的fd_set必须使用FD_ZERO()进行置空操作,然后通过FD_SET()添加文件描述符到集合中。代码中FD_ISSET()函数的作用是检查文件描述符是否在集合中,因为集合中只添加了一个文件描述符,因此只要select返回可操作,则可以认定是是当前文件描述符的返回结果,即可对文件或缓冲区数据做读取操作,反之,写入操作只需要创建写的fd_set传入低三个参数即可获取文件是否能够写入数据的状态。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值