码迷,mamicode.com
首页 > 其他好文 > 详细

I/O多路转接之 select

时间:2016-08-12 22:11:15      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:网络   select   复用   


系统提供select函数来实现多路复用输入/输出模型。


作用:select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。


函数原型:

技术分享

参数说明:

int nfds:需要监视的最大文件描述符值+1;

fd_set *readfds & *writefds & *exceptfds:指向文件描述符的指针;这三个描述符集说明了我们关心的可读、可写或处于异常条件的各个描述符;

struct timeval *timeout:需要等待的时间,为NULL,则select一直阻塞,知道某个文件描述符发生了事件,为0,仅仅检测描述集合的状态,立即返回,不等待外部事件的发生。

struct timeval{
    long tv_sec;        /* seconds */
    long tv_usec;       /* microseconds */
};

函数返回值:

成功:返回就绪描述符的个数,0表示timeout结束,没有描述符完成就绪;

失败:-1,此时状态描述参数和timeout都变成不可预测的,错误信息存在errno中。



对fd_set数据类型可以进行的处理是:

分配一个这种类型的变量;

将这种类型的一个变量值赋予同类型的另一个变量;或对于这种类型的变量使用下列四个函数中的一个。

void FD_CLR(int fd, fd_set *set);    //清除描述词组set中相关的fd的位
int FD_ISSET(int fd, fd_set *set);   //测试描述词组set中相关fd的位是否为真
void FD_SET(int fd, fd_set *set);    //设置描述词组set中相关的fd位
void FD_ZERO(fd_set *set);           //清除描述词组set的全部位


代码:

#include <stdio.h>                                                                                                                                                                                                                          
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <netinet/in.h>

int fds[128];
const int len = 128;

static void usage(const char* proc)
{
    printf("Usage: %s [ip] [port]\n",proc);
}

int startup(const char *_ip, int _port)
{
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if( sock < 0){
        perror("socket");
        exit(2);
    }

    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(_port);
    local.sin_addr.s_addr = inet_addr(_ip);

    if( bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0){
        perror("bind");
        exit(3);
    }

    if( listen(sock, 5) < 0){
        perror("listen");
        exit(4);
    }

    return sock;
}
int main(int argc, char *argv[])
{
    if(argc != 3){
        usage(argv[0]);
        exit(1);
    }

    int i = 0;
    for(; i < len; i++){
        fds[i] = -1;
    }

    int listen_sock = startup(argv[1], atoi(argv[2]));

    //FD_SET(listen_soxk, &rfds);
    //int max_fd = listen_sock;
    
    fd_set rfds;//read fd set;
    fds[0] = listen_sock;
    
    int done = 0;
    while(!done){
        int max_fd = -1;
        FD_ZERO(&rfds);
        for(i = 0; i < len; i++){
            if( fds[i] != -1){
                FD_SET(fds[i], &rfds);
            }
            max_fd = fds[i] > max_fd?fds[i]:max_fd;
        }
        struct timeval timeout = {5, 0};
        switch(select(max_fd+1, &rfds, NULL, NULL, NULL)){
            case 0:
                printf("timeout\n");
                break;
            case -1:
                perror("select");
                break;
            default:
                {
                    if( FD_ISSET(listen_sock, &rfds) ){
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int new_fd = accept(listen_sock, (struct sockaddr *)&peer, &len);
                        if(new_fd > 0){
                            printf("get a new client-> %s:%d\n",inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
                            for(i - 0; i < len; i++){
                                if(fds[i] == -1){
                                    fds[i] = new_fd;
                                    break;
                                }
                            }
                            if( i == len){
                                close(new_fd);
                            }
                        }
                    }else{
                        char buf[1024];
                        for( i = 0; i < len; i++){
                            if( i != -1 && FD_ISSET(fds[i], &rfds) ){
                                ssize_t _s = read(fds[i], buf, sizeof(buf));
                                if(_s > 0){
                                    buf[_s] = ‘\0‘;
                                    printf("client %d is closed...\n", fds[i]);
                                    close(fds[i]);
                                    fds[i] = -1;
                                }else{
                                    perror("read");
                                }
                            }
                        }

                    }
                        
                }
                break;
        }
    }
    return 0;
}

select的缺点:

(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

(3)select支持的文件描述符数量太小了,默认是1024

select的优点:

(1)相较于之前多线程的方法,使用select不用创建线程,更方便

(2)select目前几乎在所有的平台上都支持,其良好跨平台支持也是它的一个优点 


本文出自 “七百七十七快” 博客,谢绝转载!

I/O多路转接之 select

标签:网络   select   复用   

原文地址:http://10324228.blog.51cto.com/10314228/1837181

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!