I/O多路转接----select

我们知道系统IO中有五种模型:阻塞I/O, 非阻塞I/O,信号驱动I/O,以及多路复用(select,poll,epoll)这四种是同步模型,还有一种是异步I/O模型;在I/O当中,我们知道它包含两件事情,一件事叫做等,另一件就是数据搬迁;其中这五种模型就是对事件进行等的操作;其中,多路复用(多路转接)中的三种服务器是最高效的等,所以,我们来对他们进行一个研究:

select介绍

1、select函数介绍

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

第一个参数nfds:所有文件描述符中最大值加1;
第二个参数readfds:可读事件的文件描述符集合;
第三个参数writefds:可写事件的文件描述符集合;
第四个参数exceptfds:异常事件的文件描述符集合;
第五个参数timeout:设定超市时间;
  • 其中最后四个即是输入型参数也是输出型参数,四个函数的输入与输出的含义不一样:输入表示关心那些文件描述符对应的特定事件发生;输出表示那些所关心的那些文件描述符已经就绪;
三种事件处理的宏函数
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的全部位
timeout参数介绍:
NULL: 则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
0: 仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生;
特定的时间值:如果在指定的时间段里没有发生事件,则超时返回;

select服务器端的实现

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/select.h>

int fds_array[sizeof(fd_set)*8];

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");
        return 3;
    }
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//解决server bind error的问题;

    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");
        return 4;
    }

    if(listen(sock, 10) < 0)
    {
        perror("listen");
        return 5;
    }
    return sock;
}

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        usage(argv[0]);
        return 1;
    }
    int listen_sock = startup(argv[1], atoi(argv[2]));

    int nums = sizeof(fds_array)/sizeof(fds_array[0]);
    int i = 0;
    for(;i < nums; i++)//将所有元素设置为无效
    {
        fds_array[i] = -1;
    }
    fds_array[0] = listen_sock; //将listen_sock加入到数组中;

    while(1)
    {
        int max_fd = -1;
        fd_set rfds;                //创建文件描述符集合
        FD_ZERO(&rfds);             //将所有的元素清空
        for(i = 0; i < nums; i++)
        {
            if(fds_array[i] == -1)
            {
                continue;
            }
            FD_SET(fds_array[i], &rfds);
            if(fds_array[i] > max_fd)       //寻找select中第一个参数;
                max_fd = fds_array[i];
        }
        struct timeval timeout = {5,0};
        switch(select(max_fd+1, &rfds, NULL, NULL, /*&timeout*/NULL))
        {
            case -1:
                {
                    perror("select");
                }
                break;
            case 0:
                {
                    printf("time out\n");
                }
                break;
            default :
                {
                    for(i = 0; i < nums; ++i)
                    {
                        if(fds_array[i] < 0)
                            continue;
                        if(i == 0 && FD_ISSET(fds_array[i], &rfds))
                        {
                            struct sockaddr_in client;
                            socklen_t len = sizeof(client);
                            int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);
                            if(new_sock < 0)
                            {
                                perror("accept");
                                continue;
                            }
                            printf("new client: [%s:%d]\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));


                            int j = 1;
                            for(; j < nums; j++)   //找到第一个无效的位置
                            {
                                if(fds_array[j] < 0)
                                    break;
                            }
                            if(j == nums)
                            {
                                printf("server is full\n");
                                close(new_sock);
                                break;
                            }
                            else
                            {
                                fds_array[j] = new_sock;
                            }

                        }
                        else if(i != 0 && FD_ISSET(fds_array[i], &rfds))
                        {
                            char buf[1024];
                            ssize_t s = read(fds_array[i], buf, sizeof(buf)-1);
                            if(s > 0)
                            {
                                buf[s] = 0;
                                printf("client #:%s", buf);
                            }
                            else if(s == 0)
                            {
                                printf("client quit...\n");
                                close(fds_array[i]);
                                fds_array[i] = -1;
                            }
                            else
                            {
                                perror("read");
                                close(fds_array[i]);
                                return 7;
                            }
                        }
                    }
                }
                break;
        }
    }
    close(listen_sock);
    return 0;
}

利用一个dup实现客户端

#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>

static void usage(const char* proc)
{
    printf("Usage: %s[local_ip] [local_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 1;
    }

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr= inet_addr(argv[1]);
    server.sin_port = htons(atoi(argv[2]));
    socklen_t len = sizeof(struct sockaddr_in);

    if(connect(sock, (struct sockaddr *)&server, len) < 0)
    {
        perror("connect");
        return 2;
    }
    printf("connect success...\n");
    char buf[1024];

    close(1);
    dup(sock);

    while(1)
    {
    //  printf("send###");
    //  fflush(stdout);
        ssize_t _r = read(0, buf, sizeof(buf)-1);
        if(_r > 0)
        {
            buf[_r] = 0;
            printf("%s\n", buf);
            fflush(stdout);
        }
    //  write(1, buf, strlen(buf));
    }
    close(sock);
    return 0;

}

select的优缺点

优点
  • select()的可移植性更好,在某些UNIX系统上不支持poll

  • select()对于超时值提供了更好的精度:微秒,而poll是毫秒

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

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

  • select支持的文件描述符数量太小了,默认是1024;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值