zoukankan      html  css  js  c++  java
  • select I/O多路复用

    使用select函数的套接字
    如果你想保持现有连接的同时,侦听新的连接,怎么办呢?
    普通的做法(使用recv, accept是做不到的)。当使用accetp等待新的连接时,程序是阻塞的,也就没办法再同原有连接保持通信。
    另一种做法是,使用非阻塞方式,但这会浪费了宝贵的CPU时间(你的不停的轮询轮询)。
    有没有更好的办法呢?答案是肯定的 – 使用select函数。
    select可以帮助你同时监听多个套接字。它会告诉你哪个套接字读数据就绪, 哪个套接字写数据就绪,哪个套接字发生错误。
    使用select意味着使用I/O多路技术。

    select函数头文件:
    #include <sys/time.h>
    #include <sys/types.h>
    #include <unistd.h>

    select函数原型:
    int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    详细信息可查看 man select
    select()函数的参数说明:
    numfds 是readfds,writefds,exceptfds 中fd 集合中文件描述符中最大的数字加上1。
    readfds 中的fd 集合将由select 来监视是否可以读取。
    writefds 中的fds 集合将由select 来监视是否可以写入。
    exceptfds 中的fds 集合将由select 来监视是否有例外发生。
    timeout 是用来设置等待超时的
    返回值:
    如果成功,则返回就绪的文件描述符的数量。超时一般返回0。错误返回-1。
    这个timeval 结构定义如下:
    struct timeval
    {
    int tv_sec ; /* 秒数 */
    int tv_usec ; /* 微秒 */
    } ;
    只需要将tv_sec 设置为你想等待的秒数,然后设置tv_usec 为想等待的微秒数(真正
    的时间就是tv_sec 所表示的秒数加上tv_usec 所表示的微秒数)。当select()函数返回的时候,timeval 中的时间将会被设置为执行为select()后还剩下的时间。


    如果你想知道是是否可以从标准输入和一些套接字(sockfd)中读取数据,你就可以把文件描述符和sockfd 加入readfds 中。numfds 的数值设成readfds 中文件描述符中最大的那个加上一,也就是sockfd+1(因为标准输入的文件描述符的值为0 ,所以其他任何的文件描述符都会比标准输入的文件描述符大)。
    当select()函数返回的时候,readfds 将会被修改用来告诉你哪一个文件描述符你可以用来读取数据。使用FD_ISSET() 宏,你可以选出select()函数执行的结果。

    处理fd_set的宏:
    FD_ZERO(fd_set *set)将一个文件描述符集合清零
    FD_SET(int fd, fd_set *set)将文件描述符fd 加入集合set 中 – 这样select就会去监听它的状态。
    FD_CLR(int fd, fd_set *set)将文件描述符fd 从集合set 中删除 – select不再去监听它的状态。
    FD_ISSET(int fd, fd_set *set)检测fd在fdset集合中的状态是否变化。当检测到fd状态发送变化时返回真,否则,返回假。

    下面程序,等待从标准输入输入数据,并打印所输入的数据。如果3秒内没输入(输入回车才算输入就绪),则打印超时或出错。

    View Code
    复制代码
    #include <sys/time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
        fd_set fdsets;
        FD_ZERO(&fdsets);
        FD_SET(STDIN_FILENO, &fdsets);
        struct timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;
        char buff[50];
        if (select(2, &fdsets, NULL, NULL, &timeout)>0)
        {
            if (FD_ISSET(STDIN_FILENO, &fdsets))
            {
                    scanf("%s", buff);
                    printf("has input\n");
            }
        }
        else
        {
            printf("timeout or error!\n");
        }
        return 0;
    }
            
    复制代码

    下面是使用select同时侦听多个连接的例子:

    View Code
    复制代码
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #define PORT 1234
    #define MAXSOCKFD 10
    int main(int argc, char **argv)
    {
        int sockfd,newsockfd,is_connected[MAXSOCKFD],fd;
        struct sockaddr_in addr;
        int addr_len = sizeof(struct sockaddr_in);
        fd_set readfds;
        char buffer[256];
        char msg[ ] ="Welcome to server!";
        
        if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
        {
            perror("socket"); 
            exit(1); 
        }
        bzero(&addr,sizeof(addr));
        addr.sin_family =AF_INET;
        addr.sin_port = htons(PORT);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if(bind(sockfd,&addr,sizeof(addr))<0)
        {
            perror("connect");
            exit(1);
        }
        if(listen(sockfd,3)<0)
        {
            perror("listen"); 
            exit(1); 
        }
        for(fd=0;fd<MAXSOCKFD;fd++) 
            is_connected[fd]=0;
        
        while(1)
        { 
            FD_ZERO(&readfds); 
            FD_SET(sockfd,&readfds); //用于侦听新连接
            
            for(fd=0;fd<MAXSOCKFD;fd++) 
            {
                if(is_connected[fd]) 
                    FD_SET(fd,&readfds);  //已处理的连接加入select侦听            
            }
            if(!select(MAXSOCKFD,&readfds,NULL,NULL,NULL))
                continue;
            for(fd=0;fd<MAXSOCKFD;fd++) 
            {
                if(FD_ISSET(fd,&readfds))
                {
                    if(sockfd = =fd) //一个未处理的连接
                    {
                        if((newsockfd = accept (sockfd,&addr,&addr_len))<0) 
                            perror("accept");
                        write(newsockfd,msg,sizeof(msg));
                        is_connected[newsockfd] =1;
                        printf("cnnect from %s\n",inet_ntoa(addr.sin_addr));
                    }
                    else //已处理的连接有读就绪
                    { 
                        bzero(buffer,sizeof(buffer)); 
                        if(read(fd,buffer,sizeof(buffer))<=0) //读数据失败,表明连接关闭
                        {
                            printf("connect closed.\n");
                            is_connected[fd]=0;
                            close(fd);
                        }
                        else //成功读取数据
                            printf("%s",buffer);
                    }
                }
            }
        }
    }
    复制代码
  • 相关阅读:
    《分析的艺术》读书笔记
    MySql数据库文件frm的移植
    R语言学习笔记(一)
    GATE使用笔记(使用自带的GUI界面)
    如何解决 Endnote自动搜索word中中括号[ ]或者大括号{}内的文字
    java import中jar包的位置
    hello
    filebeatkafkaelk搭建
    android note3
    iOS 6 SDK: 在应用内展示App Store
  • 原文地址:https://www.cnblogs.com/spinsoft/p/2596857.html
Copyright © 2011-2022 走看看