selectI实现I/O复用

发布时间:2020-07-27 19:28:37 作者:小止1995
来源:网络 阅读:1095

系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。

select:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

selectI实现I/O复用

参数含义:

timeout:它告知内核等待所制定描述符中的任何一个就绪可在多长时间,其timeval结构用于指定这段时间的秒数和微妙数。

struct timeval

{

    long tv_sec;//seconds

    long tv_usec;//microseconds

}

有三种情况:

  1. 永远等待下去:仅在有一个描述符准备好I/O时才返回,为此,我们把该参数设为NULL.

  2. 等待固定时间:在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。

  3. 根本不等待:检查描述符后立即返回,这称为轮询,为此该参数必须指向一个timeval结构,而且其中的定时器值必须为0;

前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回。

注:有的Linux会在select函数返回时修改timeval结构,从移植性考虑,我们应假设timeval结构在函数返回时未定义,因而每次调用select前都得对它进行初始化。
中间的三个参数readset,writeset和exceptset制定我们要内核测试读,写,异常条件的描述符。

异常原因:

   1.某个套接字的带外数据的到达

   2.某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息

select使用描述符集,通常是一个×××数组,其中每个整数中每一位对应一个描述符

四个宏:selectI实现I/O复用

maxfd:待测试描述符个数:待测试描述符加1,描述符1,2...到maxfd-1均被测试。

<sys/select.h>中FD_SETSIZE常值是数据类型fd_set中描述符总数,其值通常是1024.

select函数修改三个描述符集,参数是值-结果参数,调用时,是关心描述符值。返回时,指示那些描述符已就绪。

描述符中任何与未就绪描述符对应的位返回时均被清为0,因此每次重调时,再次把所有描述符集中所关心的位置1.

返回值:大于0:所有描述符中已就绪的总位数。

    等于0:超时

    -1:出错

就绪条件:

selectI实现I/O复用

//监视输入输出 

#include<stdio.h>                                                        
#include<stdlib.h>
#include<errno.h>
#include<sys/time.h>
#include<sys/select.h>
#include<string.h>
int main()
{
    int read_fd=0;
    int write_fd=1;
    fd_set reads;
    fd_set writes;
    FD_ZERO(&reads);
    FD_ZERO(&writes);
    struct timeval _timeout={5,0};
    int ret=-1;
    int max_fd=write_fd;
    char buf[1024];
    while(1)
    {
         FD_SET(read_fd,&reads);
         FD_SET(write_fd,&writes);
         _timeout.tv_sec=5;
         _timeout.tv_usec=0;
         ret=select(max_fd,&reads,&writes,NULL,&_timeout);
         switch(ret)
         {
             case -1://error
                perror("select");
                break;
             case 0://time out
                printf("time is out...\n");
                break;
             default://normal
                {
                    ssize_t _s;
                    if(FD_ISSET(read_fd,&reads))
                    {
                        _s=read(0,buf,sizeof(buf)-1);
                        buf[_s]='\0';
                        if(strncmp(buf,"quit",4)==0)
                        {
                            printf("quit\n");
                            return 1;
                        }
                        printf("echo: %s",buf);
                    }
                    if(FD_ISSET(write_fd,&writes))
                    {
                        strcpy(buf,"hello world");
                        printf("show: %s\n",buf);
                    }
                }
                break;
         }
     }
    return 0;

运行截图:

selectI实现I/O复用

实现TCP通信,处理任意个客户的单进程,而不是为每一个客户派生一个子进程。

  1. 创建监听套接字并初始化:调用socket,bind,listen,唯一描述符是监听描述符初始化数据结构。

  2. 阻塞于select:select等待某个事件发生或新客户连接的建立或是数据,FIN或RST的到达。

  3. accept新连接

     如果监听套接字变为可读,那么已建立一个新的连接,我们调用accept并更新相应数据结构。使用fds数组中第一个未用项记录这个已连接描述符。

  4. 检查现有连接

    对于每个现有客户连接,我们要测试其描述符是否在select返回描述符集中,如果是就从该客户读取一行文本,并回显,输出。如果该客户关闭了连接,那么read将返回0,更新数据结构。
//server
#include<stdio.h>                                                        
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/time.h>
#include<sys/types.h>
#include<sys/socket.h>
#define _BACKLOG_ 5
#define _SIZE_ 64
void Usage(const char* proc)
{
    printf("%s [ip][port]\n",proc);
}
int Start(const char* _ip,int _port)
{
    if(_ip==NULL)
        return -1;
    int sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_addr.s_addr=inet_addr(_ip);
    local.sin_port=htons(_port);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
    {
        perror("bind");
        return 2;
    }
    if(listen(sock,_BACKLOG_)<0)
    {
        perror("listen");
        return 3;
    }     
    return sock;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    char* ip=argv[1];
    int port=atoi(argv[2]);
    int listen_sock=Start(ip,port);
    //allsets存储旧的描述符集
    fd_set reads,allsets;
    int fds[_SIZE_]={0};
    int max_fd=listen_sock;
    FD_ZERO(&reads);
    FD_ZERO(&allsets);
    FD_SET(listen_sock,&allsets);
    fds[0]=listen_sock;
    int i=0;                                            
    for(i=1;i<_SIZE_;++i)//init fds
    {
        fds[i]=-1;
    }
    struct timeval _timeout={5,0};
    struct sockaddr_in remote;
    socklen_t size=sizeof(remote);  
    char buf[1024];
    ssize_t _s;
    while(1)
    {
        reads=allsets;  
        _timeout.tv_sec=5;
        _timeout.tv_usec=0;
        for(i=0;i<_SIZE_;++i)
        {
            if(fds[i]>max_fd)
                max_fd=fds[i];
        }
        switch(select(max_fd+1,&reads,NULL,NULL,&_timeout))   
        {
            case -1://error
                perror("select");
                return 2;
            case 0://timeout
                printf("time is out...\n");
                break;
            default://normal
            {
                //printf("have a one is comming");//test
                for(i=0;i<_SIZE_;++i)
                {
                    if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads))
                    {

                        int newsock=accept(listen_sock,(struct sockaddr*)&remote,&size);
                        if(newsock<0)
                        {                  
                        perror("accept");
                            continue;
                        }
                        FD_SET(newsock,&allsets);
                        int j;
                        for(j=0;j<_SIZE_;++j)
                        {
                            if(fds[j]==-1)
                            {
                                fds[j]=newsock;
                                break;
                            }
                        }
                        if(j==_SIZE_)//full
                            close(newsock);
                        break;
                    }
                    else if(fds[i]>0&&FD_ISSET(fds[i],&reads))
                    {
                        _s=read(fds[i],buf,sizeof(buf)-1);    
                         if(_s==0)
                        {
                            fds[i]=-1;
                            FD_CLR(fds[i],&allsets);
                            close(fds[i]);
                            break;
                        }
                        buf[_s]='\0';
                        printf("client:%s",buf);
                        write(fds[i],buf,_s);
                    }
                    else
                    {}
                }
            }
            break;
        }
    }
    for(i=0;i<_SIZE_;++i)             
    {
        if(fds[i]>0)
        {
            close(fds[i]);
        }
    }
    return 0;
}                
//client
#include<stdio.h>                                                       
#include<stdlib.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
void Usage(const char* proc)
{
    printf("%s [ip][port]",proc);
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        Usage(argv[0]);
        return 1;
    }
    int client_sock=socket(AF_INET,SOCK_STREAM,0);
    if(client_sock<0)
    {
        perror("socket");
        return 1;
    }
    struct sockaddr_in client;
    client.sin_family=AF_INET;
    client.sin_port=htons(atoi(argv[2]));
    client.sin_addr.s_addr=inet_addr(argv[1]);
    char buf[1024];
    ssize_t _s;
    if(connect(client_sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
        perror("connection");
        return 2;
    }
    while(1)
    {
        printf("please enter:\n");
        _s=read(0,buf,sizeof(buf)-1);        
        if(_s>0)
            buf[_s]='\0';
        if(strncmp(buf,"quit",4)==0)
        {
            printf("client is quit\n");
            break;
        }
        write(client_sock,buf,_s);
        _s=read(client_sock,buf,sizeof(buf)-1);
        if(_s>0)
        {
            buf[_s]='\0';
            printf("server->client: %s",buf);
        }
    }
    close(client_sock);

    return 0;
}

selectI实现I/O复用

//回显的

selectI实现I/O复用


推荐阅读:
  1. 如何使用JavaScript获取select中的text值
  2. 架构修炼之道 | 一个传统网关系统有几种 “死” 法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

select sel

上一篇:CCNA学习笔记4---路由器启动过程和寄存器值作用

下一篇:SCCM部署(一)---环境介绍

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》