基于epoll模型的server/client

 对于epoll来说,当产生epoll调用时,不单是进行了epoll调用。在调用epoll_create()的时候创建了epoll模型,epoll模型分为三部分,红黑树,就绪队列,回调机制。

红黑树,注册epoll事件就是将事件添加至红黑树中,并将想关心的事件也添加进去,该红黑树以文件描述符作为key值;

就绪队列,就绪队列里存放是就绪的文件描述符,当文件描述符就绪时会将其拷贝至就绪队列;

回调机制:底层的一种机制

epoll_ctl()则是对红黑树中文件描述符的操作,可以添加,可以修改,也可以删除。

epoll_wait就是将所有的就绪的文件描述符从内核事件表(相当于就绪队列)中拿出来并放到它的第二个参数的数组中。所以数组中存放的的就会是所有已就绪的文件描述符,而没有其他。极大的提高了查询就绪文件描述符的效率。

 ////////////////////////////////////////client.c/////////////////////////////////////

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

int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("Usage :./select_client [port]");
        return 1;
    }

    //
    struct sockaddr_in client;
    client.sin_port = htons(atoi(argv[1]));
    client.sin_family = AF_INET;
    client.sin_addr.s_addr = htonl(INADDR_ANY);
    //创建套接字
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {
        perror("socket");
        return 1;
 }
    //连接服务器
    if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
    {
        perror("connect");
        return 1;
    }
    for(;;)
    {
        //输入消息并刷新缓冲区
        printf("client >");
        fflush(stdout);
        //将消息读到buf里
        char buf[1024] = {0};
        read(0,buf,sizeof(buf)-1);
        //将消息写给文件描述符
        if(write(sock,buf,strlen(buf))<0){
            perror("write");
            continue;
        }
        //将服务器返回的消息写到buf里   
        int ret = read(sock,buf,sizeof(buf)-1);
        if(ret<0){
            perror("read");
            continue;
        }
 if(ret==0)
        {
            printf("server close\n");
            break;
        }
        printf("server:%s\n",buf);
    }
    close(sock);
    return 0;
}

/////////////////////////////

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


int startup(int port)
{
    int sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock<0)
    {   
        perror("socket");
        exit(3);
    }   
    int opt = 1;
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons(port);
    if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
{
        perror("bind");
        exit(4);
    }
    if(listen(sock,5)<0)
    {
        perror("listen");
        exit(5);
    }
    return sock;

}
void handler_events(int epfd,struct epoll_event revs[],int num,int listen_sock)
{
    int i = 0;
    struct epoll_event ev;
    for(;i<num;i++)
    {
        int fd = revs[i].data.fd;
        if(fd==listen_sock && (revs[i].events & EPOLLIN)){
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int new_sock = accept(fd,(struct sockaddr*)&client,&len);
            if(new_sock<0)
            {
                perror("accept");
 continue;
            }
            //获得新连接不能直接读,否则会造成阻塞
            printf("get new link\n");

            ev.events = EPOLLIN;
            ev.data.fd = new_sock;
            epoll_ctl(epfd,EPOLL_CTL_ADD,new_sock,&ev);//将获得的新连接添加到epoll模型中

            continue;
        }
        if(revs[i].events & EPOLLIN){
            char buf[10240];
            ssize_t s = read(fd,buf,sizeof(buf)-1);
            if(s>0){
                buf[s]=0;
                printf("%s",buf);
                ev.events = EPOLLOUT;
                ev.data.fd = fd;
                epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
            }
            else if(s==0){
                printf("client quit");//客户端退出
                close(fd);//关闭文件描述符
                epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);//将文件描述符从epoll模型中去掉
            }
 }
            else{
                perror("read");
                close(fd);//关闭文件描述符
                epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);//将文件描述符从epoll模型中去掉
            }
            continue;
        }
        if(revs[i].events & EPOLLOUT){
            const char* echo = "HTTP/1.1 200 OK\r\n\r\n<html>hello epoll !</html>\n";
            write(fd,echo,strlen(echo));
            //写完之后
            close(fd);//关闭文件描述符
            epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);//将文件描述符从epoll模型中去掉
        }
    }

}
//eopll_server 8080
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        printf("Usage:%s port\n",argv[0]);
        return 1;
    }
int epfd = epoll_create(256);//绝对是3
    if(epfd<0)
    {
        perror("epoll_create");
        return 2;
    }
    int listen_sock = startup(atoi(argv[1]));

    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;//把listen_sock托管起来

    epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);

    struct epoll_event revs[128];
    int n = sizeof(revs)/sizeof(revs[0]);
    int timeout = 1000;
    int num = 0;

    for(;;){
        switch((num = epoll_wait(epfd,revs,n,timeout))){
            case -1:
                perror("epoll_wait");
                break;
            case 0:
                printf("timeout\n");
                break;
            default:
                handler_events(epfd,revs,num,listen_sock);
                break;

        }
    }

    close(epfd);
    close(listen_sock);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36474990/article/details/81204052