I/O复用

I/O复用使得程序可以同时监听多个文件描述符,这对提高程序的性能至关重要。需要指出的是I/O复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依次处理其中的每一个文件描述符,这使得服务器程序看起来是串行工作的。如果要实现并发,只能使用多进程或多线程等手段。

Linux下实现I/O复用的系统调用主要有select、poll、epoll。

1、select系统调用

select系统调用的用途是,在一定指定时间内,监听用户感兴趣的文件描述符上的可读、可写和异常事件。

select系统调用的原型如下:

#include<sys/select.h>

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

1)nfds参数指定被监听的文件描述符总数。通常它被设置为select监听的所有文件描述符中的最大值加1,因为文件描述符是从0开始计数的。

2)readfds,writefds和exceptfds参数分别指向可读、可写和异常等事件对应的文件描述符集合。应用程序调用select函数时,通过这三个函数传入自己感兴趣的文件描述符。select调用返回时,内核将修改通知应用程序那些文件描述符已经就绪。

3)timeout参数用来设置select函数的超时时间。它是一个timeval结构类型的指针,采用指针参数是因为内核将修改它以告诉用用程序select等待了多久。不过我们不能完全信任select调用返回后的timeout值,比如调用失败时timeout值是不确定的。

select成功时返回就续文件描述符(可读、可写、异常)的总数,如果在超时时间内没有任何文件描述符就续。select返回0。select失败时返回-1。如果在select等待期间,程序接收到信号,则select立即返回-1。

代码实例:

clic.c

#include<stdio.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<string.h>
int main(int argc,char *argv[])
{
    int sockcli = socket(AF_INET,SOCK_STREAM,0);
    if(sockcli == -1)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in addrSer;
    addrSer.sin_family = AF_INET;
    addrSer.sin_port = htons(atoi(argv[2]));
    addrSer.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t addrlen = sizeof(struct sockaddr);
    int ret = connect(sockcli,(struct sockaddr*)&addrSer,addrlen);
    if(ret == -1)
    {
        printf("Client connect Server fail\n");
        return -1;
    }
    else
        printf("Client Connect Server success\n");
    char mess[256];
    char recvbuff[256];
    while(1)
    {
        printf("Enter message:");
        scanf("%s",mess);
        send(sockcli,mess,strlen(mess)+1,0);
        recv(sockcli,recvbuff,256,0);
        printf("From server self:>%s\n",recvbuff);
    }
    close(sockcli);
    return 0;
}

ser.c

utili.h

#pragma once
#include<stdio.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<string.h>
#define LISTEN_QUEUE_SIZE 5
typedef 
int startup(char *ip,short port)
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        perror("create socket fail");
        return -1;
    }
    int yes = 1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int));
    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr=inet_addr(ip);
    socklen_t addrlen = sizeof(struct sockaddr);
    bind(sockfd,(struct sockaddr*)&address,addrlen);
    listen(sockfd,LISTEN_QUEUE_SIZE);
    return sockfd;
}
#include"utili.h"
#include<sys/select.h>
#define MAX_CLIENT_NUM 10
void data_handler(int sockconn)
{
    char mess[256];
    recv(sockconn,mess,256,0);
    printf("recv mess:>%s\n",mess);
    send(sockconn,mess,strlen(mess)+1,0);
}
int main(int argc ,char *argv[])
{
    int sockSer = startup(argv[1],atoi(argv[2]));
    fd_set readset;
    int max_sock=sockSer;
    int client_conn_num = 0;
    int client_fd[MAX_CLIENT_NUM]={0};
    while(1)
    {
        FD_ZERO(&readset);
        FD_SET(sockSer,&readset);
        int i;
        for(i=0;i<MAX_CLIENT_NUM;i++)
        {
            if(client_fd[i]!=0)
            {
                FD_SET(client_fd[i],&readset);
            }
        }
        int ret = select(max_sock+1,&readset,NULL,NULL,NULL);
        if(ret == -1)
        {
            perror("select");
            continue;
        }
        else if(ret == 0)
        {
            printf("server time out");
            continue;
        }
        else
        {
            if(FD_ISSET(sockSer,&readset))
            {
                struct sockaddr_in addrCli;
                socklen_t addrlen = sizeof(struct sockaddr);
                int sockconn = accept(sockSer,(struct sockaddr*)&addrCli,&addrlen);
                if(sockconn == -1)
                {
                    perror("accept");
                    continue;
                }
                if(client_conn_num < MAX_CLIENT_NUM)
                {
                    client_fd[client_conn_num++]=sockconn;
                    if(sockconn>max_sock)
                    {
                        max_sock = sockconn; 
                    }
                }
                else
                {
                    printf("server over load\n");
                }

            }

            else
            {
                for(i=0;i<client_conn_num;i++)
                {
                    if(FD_ISSET(client_fd[i],&readset))
                    {
                        data_handler(client_fd[i]);
                    }
                }
            }
        }

    }
    close(sockSer);
    return 0;
}

2、poll 系统调用

poll系统调用和select类似,也是在指定时间内轮训一定数量的文件描述符,以测试其中是否有就绪者。poll的原型如下:

#include<poll.h>

int poll(struct pollfd *fds,nfds_t nfds,int timout);

1)fds是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。pollfd结构体的定义如下:

struct pollfd
{
	int fd;                   /* 文件描述符 */
	short events;             /* 注册的事件 */
	short revents;            /* 实际发生的事件,由内核填充*/
}

2)nfds指定被监听集合fds的大小。

3)timeout参数指定poll的超时值,单位是毫秒。当timeout为-1时,poll将永远阻塞,直到某个事件发生,当timeout为0时,poll调用将立即返回。

ser.c

#include"../utili.h"
#include<poll.h>
#define MAX_CLIENT_NUM 5
void data_handler(int sockconn)
{
    while(1)
    {
        char message[256];
        recv(sockconn,message,256,0);
        printf("mess>>%s\n",message);
        send(sockconn,message,strlen(message)+1,0);
    }
}
int main(int argc,char *argv[])
{
    int sockSer = startup(argv[1],atoi(argv[2]));
    struct pollfd client_fd[MAX_CLIENT_NUM];
    client_fd[0].fd = sockSer;
    client_fd[0].events = POLLIN;
    int i;
    for(i=1;i<MAX_CLIENT_NUM;i++)
    {
        client_fd[i].fd=0;
    }
    int num = 1;
    while(1)
    {
        //printf("aaaaaaaaaaaaaaaaaaa\n");
        int ret = poll(client_fd,num,-1);
        // printf("bbbbbbbbbbbbbbbb\n");
        if(ret == -1)
        {
            perror("poll");
            continue;
        }
        else if(ret == 0)
        {
            printf("server time out");
            continue;
        }
        else
        {
            int i;
            if(client_fd[0].revents & POLLIN)
            {
                struct sockaddr_in addrcli;
                socklen_t addrlen = sizeof(struct sockaddr);
                int sockconn = accept(sockSer,(struct sockaddr*)&addrcli,&addrlen);
                if(sockconn == -1)
                {
                    perror("accept");
                    continue;
                }


                for(i=0;i<MAX_CLIENT_NUM;i++)
                {
                    if(client_fd[i].fd == 0)
                    {
                        client_fd[i].fd = sockconn;
                        client_fd[i].events = POLLIN;
                        num++;
                        break;
                    }    
                }
                if(i>=MAX_CLIENT_NUM)
                {
                    printf("server over load");
                }
            }
            else 
            {
                for(i=1;i<MAX_CLIENT_NUM;i++)
                {
                    if(client_fd[i].fd == 0)
                        continue;
                    if(client_fd[i].revents&POLLIN)
                    {
                        data_handler(client_fd[i].fd);
                    }
                }
            }

        }
    }
    close(sockSer);
    return 0;
}

3、epoll系统调用




ser.c

#include<stdio.h>
#include<unistd.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include"../utili.h"
#define MAX_CLIENT_NUM 10
#define MAX_EVENT_SIZE 100
void data_handler(int sockconn)
{
    char buffer[256];
    recv(sockconn,buffer,256,0);
    printf("Msg:>%s\n",buffer);
    send(sockconn,buffer,strlen(buffer)+1,0);
}
int main(int argc,char* argv[])
{
    int sockSer = startup(argv[1],atoi(argv[2]));
    struct epoll_event event[MAX_EVENT_SIZE];
    int epoll_fd = epoll_create(MAX_EVENT_SIZE);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockSer;
    epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sockSer,&ev);
    int client_conn_num = 0;
    while(1)
    {
        int ret = epoll_wait(epoll_fd,event,MAX_EVENT_SIZE,-1);
        if(ret == -1)
        {
            perror("epoll_wait");
            continue;
        }
        else if(ret == 0)
        {
            perror("server time out");
            continue;
        }
        else 
        {
            int i;
            for(i=0;i<ret;i++)
            {
                if(event[i].data.fd == sockSer&&event[i].events&EPOLLIN)
                {
                    struct sockaddr_in addrCli;
                    socklen_t addrlen = sizeof(struct sockaddr);
                    int sockconn = accept(sockSer,(struct sockaddr*)&addrCli,&addrlen);
                    if(sockconn == -1)
                    {
                        perror("accept");
                        continue;
                        break;
                    }
                    if(client_conn_num >= MAX_CLIENT_NUM)
                    {
                        printf("server over load");
                    }
                    else
                    {
                        ev.events = EPOLLIN;
                        ev.data.fd = sockconn;
                        epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sockconn,&ev);
                        client_conn_num++;
                    }
                }

                else if(event[i].events &EPOLLIN) 
                {
                    data_handler(event[i].data.fd);
                }
            }
        }
    }
    close(sockSer);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/we_are_family678/article/details/80245792