Linux下的i/o复用——select

1、i/o复用

一个进程或者一个线程能够同时对多个文件描述符(socket)提供服务,服务器上的进程或线程如何将多个文件描述符统一监听,当任意一个文件描述符上有事件发生,其都能及时处理。

2、select函数

启动监听:int select(int nfds,fd_set *readfds , fd_set *writefds ,fd_set *excefds ,struct timeval *timeout);
每次启动select调用时都能够重新设置值readfds,writefds。
其中fd_set的结构体为
typedef struct
{
int fds_bits[32];
}fd_set;

nfds:最大文件描述符的值+1,
readfds:用户感兴趣的可读事件的文件描述符的集合,
wretefds:用户感兴趣的可写事件的文件描述符的集合,
execfds:异常事件的文件描述符集合,
timeout:设置超时时间。如果timeout为NULL,select一直阻塞。

当select()函数的返回值
大于0 :返回就绪文件描述符的个数
等于0 : 超时
等于-1 :出错

select() 返回后,如何知道那些文件描述符就绪?
以下宏就是用来识别的:
int FD_ZERO(int fd, fd_set *fdset);

int FD_CLR(int fd, fd_set *fdset);

int FD_SET(int fd, fd_set *fd_set);

int FD_ISSET(int fd, fd_set *fdset);

3、以下是select函数的一个简单应用代码

服务端来连接接收数据的代码:

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

void Init_fds(int *fds,int len)
{
    int i=0;
    for(;i<100;i++)
    {
        fds[i]=-1;
    }
}
void Insert_fd(int *fds,int fd,int len)
{
    int i=0;
    for(;i<len;i++)
    {
        if(fds[i] == -1)
        {
            fds[i] = fd;
            break;
        }
    }
}
void Delete_fd(int *fds,int fd,int len)
{
    int i=0;
    for(;i<len;i++)
    {
        if(fds[i] == fd)
        {
            fds[i]=-1;
            break;
        }
    }
}
int main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);
    
    struct sockaddr_in saddr,caddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));

    assert(res != -1);

    listen(sockfd,5);
    
    fd_set readfds;
    int fds[100];
    Init_fds(fds,100);
    Insert_fd(fds,sockfd,100);
    while(1)
    {
        int maxfd = -1;
        FD_ZERO(&readfds);
        int i=0;
        for(;i<100;i++)
        {
            if(fds[i] != -1)
            {
                if(fds[i] >maxfd)
                {
                    maxfd =fds[i];
                }
                FD_SET(fds[i],&readfds);
            }
        }
        int n = select(maxfd+1,&readfds,NULL,NULL,NULL);
        if(n<=0)
        {
            printf("select fail\n");
            continue;
        }
        for(i=0;i<100;i++)
        {
            if(fds[i] != -1&&FD_ISSET(fds[i],&readfds))
            {
                if(fds[i] ==sockfd)
                {
                    int len = sizeof(caddr);
                    int c =accept(sockfd,(struct sockaddr*)&caddr,&len);
                    if(c<0)
                    {
                        printf("one cilent link error\n");
                        continue;
                    }
                    Insert_fd(fds,c,100);
                }
                else
                {
                    int fd = fds[i];
                    char buff[128] ={0};
                    int n=recv(fd,buff,127,0);
                    if(n<=0)
                    {
                        close(fd);
                        Delete_fd(fds,fd,100);
                        continue;
                    }
                    printf("%d  %s\n",fd,buff);
                    send(fd,"OK",2,0);
                }
            }
        }
    }
}

客户端来连接发送数据的代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
int main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family =AF_INET;
    saddr.sin_port =htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
    int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    assert(res != -1);
    
    while(1)
    {
        char buff[128]={0};
        printf("Input:\n");
        fgets(buff,128,stdin);
        if(strncmp(buff,"end",3)==0)
        {
            break;
        }
        send(sockfd,buff,strlen(buff),0);
        memset(buff,0,128);
        recv(sockfd,buff,127,0);
        printf("buff=%s\n",buff);
    }
    close(sockfd);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值