I/O多路转接之select


I/O多路转接有三种实现方式:select , poll ,epoll。今天我们来了解一下select函数实现I/O多路转接。
select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。
这里写图片描述
参数1:nfds是需要监视的最大的文件描述符值+1
参数2:readfds是需要检测的可读文件描述符的集合;
参数3:writefds是需要检测的可写文件描述符的集合 ;
参数4:exceptfds是需要检测的异常文件描述符的集合;
参数5:timeout是结构体timeval,用来设置select()的等待时间;
:fd_set与struct timeval都是输入输出型参数,fd_set作输入时表示关心某个fd,做输出时表示哪个fd就绪。
timeout有三种情况:
1、NULL:仅在有一个描述符准备好I/O时才返回;
2、timeout为指定时间:在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。
3、0:检查描述符后立即返回,这称为轮询即该参数指向一个timeval结构,其中的定时器值为0;
—————————————————————————————————————————————
下面的宏提供了处理fd_set的这三种描述集的方式:
FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位;
FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真;
FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位;
FD_ZERO(fd_set *set);用来清除描述词组set的全部位;
函数返回值:
1、成功:文件描述词状态已改变的个数;
2、0:在描述词状态改变前已超过timeout时间,没有返回;
3、-1:有错误发生,错误原因存于errno;
select模型的特点:
1、可监控的文件描述符个数取决于sizeof(fd_set)的值;
2、将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一:是用于在select 返回后,array作为源数据和fd_set进行FD_ISSET判断是否有事件发生;二:是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始 select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大maxfd,给其+1用于select的第一个参数。
select模型的缺点: 
1、每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
2、每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
3、select支持的文件描述符数量太少了,默认是1024。
使用select实现一个TCP服务器:

//server端:
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/select.h>
#include<stdlib.h>
int array_rfds[1024];
static void Usage(const char* proc)
{
    printf("Usage:%s [local_ip][local_port]\n",proc);
}
int Startup(const char* _ip,int _port)
{
    int sock= socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        exit(1);
    }
    int flag = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port= htons(_port);
    server.sin_addr.s_addr = inet_addr(_ip);
    if(bind(sock,(struct sockaddr*)&server,sizeof(server))<0)
    {
        perror("bind");
        exit(2);
    }

    if(listen(sock,10)<0)
    {
        perror("listen");
        exit(3);
    }
    return sock;
}
int main(int argc,const char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int listen_sock = Startup(argv[1],atoi(argv[2]));
    fd_set rfds;
    int maxfd = 0;
    int array_rsize = sizeof(array_rfds)/sizeof(array_rfds[0]);
    array_rfds[0] = listen_sock;
    int i = 1;
    for(;i<array_rsize;++i)
    {
        array_rfds[i] = -1;
    }
    while(1)
    {
        struct timeval timeout={0,0};
        FD_ZERO(&rfds);
        maxfd = -1;
        for(i=0;i<array_rsize;++i)
        {
            if(array_rfds[i]>0)
            {
                FD_SET(array_rfds[i],&rfds);
                if(array_rfds[i]>maxfd)
                    maxfd = array_rfds[i];
            }
        }
        switch(select(maxfd+1,&rfds,NULL,NULL,/*&timeout*/NULL))
        {
            case -1:
                perror("select");
                break;
            case 0:
                printf("timeout...");
                break;
            default:
                {
                    int j = 0;
                    for(;j<array_rsize;++j)
                    {
                        if(array_rfds[j]<0)
                            continue;
                        if(j==0 && FD_ISSET(array_rfds[j],&rfds))
                        {
                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);
                            int newfd = accept(array_rfds[j],\
                                    (struct sockaddr*)&client,&len);
                            if(newfd<0)
                            {
                                perror("accept");
                                continue;
                            }
                            printf("new client--> %s:%d\n",\
                                    inet_ntoa(client.sin_addr),\
                                    ntohs(client.sin_port));
                            int k= 1;
                            for(;k<array_rsize;++k)
                            {
                                if(array_rfds[k]<0)
                                {
                                    array_rfds[k] = newfd;
                                    break;
                                }
                            }
                            if(k == array_rsize)
                            {
                                close(newfd);
                            }
                        }
                        else if(j!=0 && FD_ISSET(array_rfds[j],&rfds))
                        {
                            char buf[1024];
                            ssize_t s = read(array_rfds[j],buf,sizeof(buf)-1);
                            if(s>0)
                            {
                                buf[s] = 0;
                                printf("client say: %s\n",buf);
                            }
                            else if(s ==0)
                            {
                                printf("client is quit!!\n");
                                close(array_rfds[j]);
                                array_rfds[j] = -1;
                            }
                            else
                            {
                                perror("read");
                                close(array_rfds[j]);
                                array_rfds[j] = -1;
                            }
                        }
                    }
                }
                break;
        }
    }
    return 0;
}
//client端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void usage(const char* proc)
{
    printf("Usage:%s[server_ip][server_port]\n",proc);
}
int main(int argc,char *argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 2;
    }
    struct sockaddr_in peer;
    peer.sin_family = AF_INET;
    peer.sin_port = htons(atoi(argv[2]));
    peer.sin_addr.s_addr = inet_addr(argv[1]);
    int ret = connect(sock,(struct sockaddr*)&peer,sizeof(peer));
    if(ret<0)
    {
        perror("connect");
        printf("%s\n",strerror(ret));
        return 3;
    }
    char buf[1024];
    while(1)
    {
        printf("please enter: ");
        fflush(stdout);
        ssize_t s=read(0,&buf,sizeof(buf));
        if(s<0)
        {
            perror("read");
            return 4;
        }
        buf[s-1]=0;
        write(sock,&buf,strlen(buf));
        printf("server echo: %s\n",buf);        
    }
    close(sock);
    return 0;
}

这里写图片描述
client1:
这里写图片描述
client2:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值