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>0);
printf("fd_set size = %d
", sizeof(fd_set));
    FD_ZERO(&readfd);
while(1)
{
    timeout.tv_sec=2;
    timeout.tv_usec=0;
    FD_SET(keyboard,&readfd);
    ret=select(keyboard+1,&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,1);
        if('
'== c)
            continue;
        printf("hehethe input is %c
",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
", conn_amount);
    for (i = 0; i < BACKLOG; i++) {
        printf("[%d]:%d  ", i, fd_A[i]);
    }
    printf("

");
}

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 = 1;
    char buf[BUF_SIZE];
    int ret;
    int i;

    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    }

    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }
    
    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, '', sizeof(server_addr.sin_zero));

    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(1);
    }

    if (listen(sock_fd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    printf("listen port %d
", MYPORT);

    fd_set fdsr;
    int maxsock;
    struct timeval tv;

    conn_amount = 0;
    sin_size = sizeof(client_addr);
    maxsock = sock_fd;
    while (1) {
        // initialize file descriptor set
        FD_ZERO(&fdsr);
        FD_SET(sock_fd, &fdsr);

        // timeout setting
        tv.tv_sec = 30;
        tv.tv_usec = 0;

        // add active connection to fd set
        for (i = 0; i < BACKLOG; i++) {
            if (fd_A[i] != 0) {
                FD_SET(fd_A[i], &fdsr);
            }
        }

        ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
        if (ret < 0) {
            perror("select");
            break;
        } else if (ret == 0) {
            printf("timeout
");
            continue;
        }

        // check every fd in the set
        for (i = 0; i < conn_amount; i++) {
            if (FD_ISSET(fd_A[i], &fdsr)) {
                ret = recv(fd_A[i], buf, sizeof(buf), 0);
                if (ret <= 0) {        // client close
                    printf("client[%d] close
", i);
                    close(fd_A[i]);
                    FD_CLR(fd_A[i], &fdsr);
                    fd_A[i] = 0;
                } else {        // receive data
                    if (ret < BUF_SIZE)
                        memset(&buf[ret], '', 1);
                    printf("client[%d] send:%s
", 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 <= 0) {
                perror("accept");
                continue;
            }

            // add to fd queue
            if (conn_amount < BACKLOG) {
                fd_A[conn_amount++] = new_fd;
                printf("new connection client[%d] %s:%d
", 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
");
                send(new_fd, "bye", 4, 0);
                close(new_fd);
                break;
            }
        }
        showclient();
    }

    // close other connections
    for (i = 0; i < BACKLOG; i++) {
        if (fd_A[i] != 0) {
            close(fd_A[i]);
        }
    }

    exit(0);
}

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

原文地址:https://www.cnblogs.com/youxin/p/4031009.html