• 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

  • 相关阅读:
    (兼容IE8)的渐变
    左侧固定,右侧自适应,两列等高并且自适应的第二种办法
    左侧定宽,右侧自适应,两列布局且等高
    下拉框重写
    在页面中输出当前客户端时间
    用哈希表去数组重复项,有详细注释
    求数组最大值、求和、乘法表、取整
    类似新浪微博输入字符计数的效果
    将博客搬至CSDN
    Mysql常用操作
  • 原文地址:https://www.cnblogs.com/youxin/p/4031009.html
Copyright © 2020-2023  润新知