linux select

时间:2023-03-09 01:42:16
linux select

man select:

#include <sys/select.h>

#include <sys/time.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the
file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is
considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without block‐
ing.

最后一个参数,他告知内核等待所指定描述字中的任何一个就绪可花多长时间。其timeval结构用于指定这段时间的秒数和微秒数。

struct timeval
     {
             time_t tv_sec;
             time_t tv_usec;
     };
     这里第一个域的单位为秒,第二个域的单位为微秒。

nfds

需要检查的文件描述字个数(即检查到fd_set 的第几位),数值应该比三组fd_set中所含的最大fd值更大,一般设为三组fd_set中所含的最大fd值加1(如在readset, writeset,exceptset中所含最大的fd为5,则nfds=6,因为fd是从0开始的)。设这个值是为提高效率,使函数不必检查 fd_set的所有1024位。

readset

来检查可读性的一组文件描述字。

writeset

用来检查可写性的一组文件描述字。

exceptset

用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)

timeout

有三种可能:

1. timeout="NULL"(阻塞:直到有一个fd位被置为1函数才返回)

2. timeout所指向的结构设为非零时间(等待固定时间:有一个fd位被置为1或者时间耗尽,函数均返回)

3. timeout所指向的结构,时间设为0(非阻塞:函数检查完每个fd后立即返回)

select函数作用:在timeout时间内,不断测试不超过nfds 的所有fd,对于每一个接受到外部事件的fd, 将其在fd_set中的位置置1, 其余的没有接收到外部条件的fd位置置0,通外接下来的FD_ISSET进行测试,找到满足条件的所有fd。所以每次在调用select函数之前,要重新对fd_set进行赋值。

select函数返回值:

如果在timeout时间内,有fd满足条件,返回对应位仍然为1的fd的总数。

如果timeout时间用完了,也没有fd接收到外部事件,则返回0

出错的情况返回负数。

四个宏来操作: 完全一点 从accept开始.

fd_set set;

FD_ZERO(&set); /* 将set清零使集合中不含任何fd*/

FD_SET(fd, &set); /* 将fd加入set集合 */

FD_CLR(fd, &set); /* 将fd从set集合中清除 */

FD_ISSET(fd, &set); /* 测试fd是否在set集合中*/

过去,一个fd_set通常只能包含<32的fd(文件描述 字),因为fd_set其实只用了一个32位矢量来表示fd;现在,UNIX系统通常会在头文件中定义常量 FD_SETSIZE,它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。根据fd_set的位矢量实 现,我们可以重新理解操作fd_set的四个宏:

fd_set set;

FD_ZERO(&set); /*将set的所有位置0,如set在内存中占8位则将set置为

00000000*/

FD_SET(0, &set); /* 将set的第0位置1,如set原来是00000000,则现在变为10000000,这样fd==1的文件描述字就被加进set中了 */

FD_CLR(4, &set); /*将set的第4位置0,如set原来是10001000,则现在变为10000000,这样fd==4的文件描述字就被从set中清除了 */

FD_ISSET(5, &set); /* 测试set的第5位是否为1,如果set原来是10000100,则返回非零,表明fd==5的文件描述字在set中;否则返回0*/

在有了select后可以写出像样的网络程序来!举个简单的例子,就是从网络上接受数据写入一个文件中。

例子:

main()

{

int sock;

FILE *fp;

struct fd_set fds;

struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0

char buffer[256]={0}; //256字节的接收缓冲区

while(1)

{

FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化

FD_SET(sock,&fds); //添加描述符

FD_SET(fp,&fds); //同上

maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1

switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用

{

case -1: exit(-1);break; //select错误,退出程序

case 0:break; //再次轮询

default:

if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据

{

recvfrom(sock,buffer,256,.....);//接受网络数据

if(FD_ISSET(fp,&fds)) //测试文件是否可写

fwrite(fp,buffer...);//写入文件

buffer清空;

}// end if break;

}// end switch

}//end while

}//end main

参考:

http://blog.sina.com.cn/s/blog_5c8d13830100pwaf.htm

将标准输入keyboard作为fd加入到fd_set中去。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
using namespace std;
int main ()
{
int keyboard;
int ret,i;
char c;
fd_set readfd;
struct timeval timeout;
keyboard = open("/dev/tty",O_RDONLY | O_NONBLOCK);
assert(keyboard>);
printf("fd_set size = %d\n", sizeof(fd_set));
FD_ZERO(&readfd);
while()
{
timeout.tv_sec=;
timeout.tv_usec=;
FD_SET(keyboard,&readfd);
ret=select(keyboard+,&readfd,NULL,NULL,&timeout);
//ret=select(keyboard+1,&readfd,NULL,NULL,NULL);
printf("FD_ISEET before = %d, tv_sec =%d, tv_usec =%d", FD_ISSET(keyboard, &readfd),
timeout.tv_sec, timeout.tv_usec);
if(FD_ISSET(keyboard,&readfd))
{
read(keyboard,&c,);
if('\n'== c)
continue;
printf("hehethe input is %c\n",c);
if ('q'==c)
break;
}
}
}

例子2:

使用select函数可以以非阻塞的方式和多个socket通信。程序只是演示select函数的使用,功能非常简单,即使某个连接关闭以后也不会修改当前连接数,连接数达到最大值后会终止程序。

1. 程序使用了一个数组fd_A,通信开始后把需要通信的多个socket描述符都放入此数组。

2. 首先生成一个叫sock_fd的socket描述符,用于监听端口。

3. 将sock_fd和数组fd_A中不为0的描述符放入select将检查的集合fdsr。

4. 处理fdsr中可以接收数据的连接。如果是sock_fd,表明有新连接加入,将新加入连接的socket描述符放置到fd_A。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define MYPORT 1234 // the port users will be connecting to #define BACKLOG 5 // how many pending connections queue will hold #define BUF_SIZE 200 int fd_A[BACKLOG]; // accepted connection fd
int conn_amount; // current connection amount void showclient()
{
int i;
printf("client amount: %d\n", conn_amount);
for (i = ; i < BACKLOG; i++) {
printf("[%d]:%d ", i, fd_A[i]);
}
printf("\n\n");
} int main(void)
{
int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd
struct sockaddr_in server_addr; // server address information
struct sockaddr_in client_addr; // connector's address information
socklen_t sin_size;
int yes = ;
char buf[BUF_SIZE];
int ret;
int i; if ((sock_fd = socket(AF_INET, SOCK_STREAM, )) == -) {
perror("socket");
exit();
} if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -) {
perror("setsockopt");
exit();
} server_addr.sin_family = AF_INET; // host byte order
server_addr.sin_port = htons(MYPORT); // short, network byte order
server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero)); if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -) {
perror("bind");
exit();
} if (listen(sock_fd, BACKLOG) == -) {
perror("listen");
exit();
} printf("listen port %d\n", MYPORT); fd_set fdsr;
int maxsock;
struct timeval tv; conn_amount = ;
sin_size = sizeof(client_addr);
maxsock = sock_fd;
while () {
// initialize file descriptor set
FD_ZERO(&fdsr);
FD_SET(sock_fd, &fdsr); // timeout setting
tv.tv_sec = ;
tv.tv_usec = ; // add active connection to fd set
for (i = ; i < BACKLOG; i++) {
if (fd_A[i] != ) {
FD_SET(fd_A[i], &fdsr);
}
} ret = select(maxsock + , &fdsr, NULL, NULL, &tv);
if (ret < ) {
perror("select");
break;
} else if (ret == ) {
printf("timeout\n");
continue;
} // check every fd in the set
for (i = ; i < conn_amount; i++) {
if (FD_ISSET(fd_A[i], &fdsr)) {
ret = recv(fd_A[i], buf, sizeof(buf), );
if (ret <= ) { // client close
printf("client[%d] close\n", i);
close(fd_A[i]);
FD_CLR(fd_A[i], &fdsr);
fd_A[i] = ;
} else { // receive data
if (ret < BUF_SIZE)
memset(&buf[ret], '\0', );
printf("client[%d] send:%s\n", i, buf);
}
}
} // check whether a new connection comes
if (FD_ISSET(sock_fd, &fdsr)) {
new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
if (new_fd <= ) {
perror("accept");
continue;
} // add to fd queue
if (conn_amount < BACKLOG) {
fd_A[conn_amount++] = new_fd;
printf("new connection client[%d] %s:%d\n", conn_amount,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (new_fd > maxsock)
maxsock = new_fd;
}
else {
printf("max connections arrive, exit\n");
send(new_fd, "bye", , );
close(new_fd);
break;
}
}
showclient();
} // close other connections
for (i = ; i < BACKLOG; i++) {
if (fd_A[i] != ) {
close(fd_A[i]);
}
} exit();
}

http://www.cnblogs.com/gentleming/archive/2010/11/15/1877976.html

http://blog.chinaunix.net/uid-26912934-id-3306946.html

http://blog.csdn.net/poechant/article/details/7627894

http://blog.csdn.net/sven_007/article/details/7909995

http://www.cnblogs.com/hjslovewcl/archive/2011/03/16/2314330.html