使用select需要注意的细节

本文通过一个实际项目案例,详细解析了在网络编程中使用Select函数时容易忽视的重要细节,特别是关于超时设置的问题,并对比了Select与Pselect的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用select需要注意的细节    

在学校的时候就使用过select,但是在项目中使用的时候却犯了个错误。

select如何使用就不进行总结了,网上教程太多,以下是项目中我写的一小段代码,便于总结。

int TvsStateManager::handleProbeStreamMsg()
{
	struct sockaddr_in addr;
	int fd, n,addrlen;
	struct ip_mreq mreq;

	char recvBuf[BUF_SIZE];

	u_int flag = 1; 

	/* create what looks like an ordinary UDP socket */
	if ((fd=socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		LogE("creat socket failure\n");
	    return -1;
	}

	/* allow multiple sockets to use the same PORT number */
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0){
	    LogE("reusing addr failure\n");
	    return -2;
	}

	/* set up destination address */
	memset(&addr,0,sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY); /* N.B.: differs from sender */
	addr.sin_port = htons(mConfig->mMultiCastStreamPort);

    /* bind to receive address */
	if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0){
	    LogE("bind socket failure\n");
	    return -3;
	}

	/* use setsockopt() to request that the kernel join a multicast group */
	mreq.imr_multiaddr.s_addr = inet_addr(mConfig->mMultiCastStreamIP.c_str());
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);

	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0){
        LogE("setsockopt join multicast group failure\n");
	    return -4;
	}
  
//    if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0){
//	    LogE("set timeout failure\n");
//        return -5;
//	}
    
    fd_set readfds;
    int maxfds = 0;
    struct timeval timeout;
    while (1){
        /*这个超时设置很关键,必须设置在里面,因为select模式,timeout会随着检查文件描述符集合状态而减小,换句话说就是用剩余的时间来更新这个结构*/
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        addrlen=sizeof(addr);
        FD_ZERO(&readfds);
        FD_SET(fd,&readfds);
        maxfds = fd +1;
        if(select(maxfds, &readfds, NULL, NULL, &timeout) > 0){
            
			if((n = recvfrom(fd, recvBuf, BUF_SIZE, 0, (struct sockaddr *)&addr, &addrlen)) > 0){
			    mProbeStream = true;
        	    mQtPanel->sendProbeStreamMsg(mProbeStream);
            }
        	bzero(recvBuf,sizeof(recvBuf));
        	if(setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0){
        	    LogE("setsockopt quit multicast failure\n");
        	    return -7;
        	}
        	close(fd);
        	break;
        }
    }

    return 0;
}
        可以看到我在使用结构struct timeval,将timeout设置放到了while里面,这样才是正确的。可能也是由于自己以前理解的不够透彻,当时我把设置timeout放到了while外面,那么引起的结果就是程序只等待一次5秒,后面却一直显示timeout不在等待5秒,测试程序就不再弄了。重新翻阅了下《unix环境高级编程》这本书,有一段不起眼的话说的很详细,如下:

     POSIX.1允许实现修改timeval结构中的值,所以在select返回后,你不能指望该结构仍旧保持调用select之前它所包含的值。FreeBSD 8.0、Mac OS X 10.6.8和Solaris 10都保持该结构中的值不变。但是,若在超时时间尚未到期时,select就返回,那么Linux 3.2.0将用剩余时间值更新该结构。

    这段话已经很明确了,select设置的时间是会随着改变的,另外如果想不让它改变,那么可以使用pselect函数,而且pselect函数超时更加精确,pselect使用的是timespec结构,timespec以秒和纳秒表示超时值,而select的timeval结构则是秒和微妙级别。另外pselect的超时值是被设置为const的,这也就保证了调用pselect不会改变此值。



    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值