欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于epoll模型的server/client

程序员文章站 2022-03-14 12:13:19
...

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

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

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

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

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

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

基于epoll模型的server/client

 

 ////////////////////////////////////////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;
}

 

 

相关标签: epoll模型