epoll的http服务器

该博客介绍了一个简易的HTTP服务器,可在Linux下直接运行。代码未处理EPOLLOUT事件,处理方法可参考相关触发条件。代码能配合线程池使用,基于EPOLLOUT例子,有很多细节未处理,仅为简单示例,代码无注释,注释可在相关帖子查看,例子仅用Firefox测试过。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个简易的http服务器 , linux下可直接运行

代码里没有处理EPOLLOUT事件,需要处理的参考:EPOLLOUT触发条件

代码可配合线程池使用 : 线程池

代码基于EPOLLOUT例子 EPOLLOUT触发条件 第2个例子

有很多细节上的东西都没处理, 只是一个简单的例子,

下面代码中没有注释, 上面连接中的帖子里都有注释;

例子只用firefox跑过;

 

 

serv.c

#include "util.h"
#include <dirent.h>
#include <wchar.h>
#define MAX_EVENTS 512
#define BUFF_SIZE 4096
typedef  struct _ev_data
{
    int fd;
    char * buffer;
    int nread;
    int start_write_index;
    int epfd;
    int events;
}ev_data;


ev_data * new_event(int fd ,int epfd,int events,int needbuff){
    ev_data * p = malloc(sizeof(ev_data));
    if(p){
        memset(p,0,sizeof(ev_data));
        if(needbuff) {
            char *buf = (char *) malloc(BUFF_SIZE);
            if (!buf) {
                free(p);
                return 0;
            }
            p->buffer = buf;
        }
        p->fd = fd;
        p->epfd = epfd;
        p->events = events;
    }
    return p;
}
void free_event(ev_data * ptr){
    if(!ptr)
        return;
    close(ptr->fd);
    if(ptr->buffer){
        free(ptr->buffer);
    }
    free(ptr);
}

int update_event(int events , void * ptr , int epfd , int op , int fd){
    static struct epoll_event ev;
    memset(&ev,0,sizeof(ev));
    ev.events = events;
    ((ev_data*)ptr)->events = events;
    ev.data.ptr = ptr;
    return epoll_ctl(epfd,op,fd,&ev);
}

static int setnonblock(int fd , int nonblock){
    int flag = fcntl(fd,F_GETFL,0);
    if(nonblock)
        return fcntl(fd,F_SETFL,flag|O_NONBLOCK);
    else
        return fcntl(fd,F_SETFL,flag&~O_NONBLOCK);
}

int write_data(ev_data * pdata);
void send_response_header(int fd, int http_err_no,int length){
    static char buf[1024];
    static char no_text[10] = "ok";
    static char type_text[50] = "text/html; charset=UTF-8";
    int len = 0;
    len+= sprintf(buf,"http/1.1 %d %s\r\n",http_err_no,no_text);
    len += sprintf(buf+len,"content-Type:%s\r\n",type_text);
    len += sprintf(buf+len,"content-length:%d\r\n",length);
    len += sprintf(buf+len,"server:localhost\r\n");
    len += sprintf(buf+len,"\r\n");
    write(fd,buf,len);
}
void send_response(int sockfd, const char *path,int is_dir){
    static char buf[8192];
    int len = 0;
    if(!is_dir) {
        int fd = open(path, O_RDONLY);
        if (fd < 0) {
            puts("open failed");
            return;
        }
        while ((len = read(fd, buf, sizeof(buf))) > 0) {
            write(sockfd, buf, len);
        }
    }
    else{
        static char fullpath[1024];
        int nlen = 0;
        len = sprintf(buf,"<html><head><title>fuckyou</title></head>");
        len += sprintf(buf+len,"<body>");
        write(sockfd,buf,len);
        struct dirent ** ent =  NULL;
        int n  = scandir(path,&ent,NULL, alphasort);
        if(n < 0){
            len = sprintf(buf,"forbidden 403 </body></html>");
            write(sockfd,buf,len);
            puts("scandir error");
            return;
        }

        while(n--){
            if(strcmp(ent[n]->d_name,".") == 0 || strcmp(ent[n]->d_name,"..") == 0)
                continue;
            nlen = sprintf(fullpath,"%s%s",path,ent[n]->d_name);
            fullpath[nlen] = 0;
            len = sprintf(buf ,"<p><a href=\"%s\">%s</a></p>" ,fullpath,ent[n]->d_name);
            free(ent[n]);
            write(sockfd,buf,len);
        }
        free(ent);
        len = sprintf(buf,"</body></html>");
        write(sockfd,buf,len);

    }

}
int is_http_get(char * pbuffer){
    if(strncasecmp("get",pbuffer,3) == 0){
        return 1;
    }
    return 0;
}
void send_error_msg(){
    static char error_msg[] = "404 not found";
    static int len = strlen(error_msg);
    send_response_header(pdata->fd,404,len);
    write(pdata->fd,error_msg,len);
}
void http_request(ev_data * pdata){
    char * pbuffer = pdata->buffer;
    static char method[5],path[1024],protocal[10];

    sscanf(pbuffer, "%[^ ] %[^ ] %[^\r\n]" , method,path,protocal);
    printf("method:%s, path:%s, protocal:%s\n"  , method , path,protocal);

    struct stat st;
    static char rpath[2048];
    int len = sprintf(rpath,".%s",path);
    rpath[len] = 0;
    printf("rpath:%s \n" , rpath );


    if( stat(rpath,&st) < 0){
        send_error_msg();
        perror("stat ");
        goto done;
    }



    //dir
    if(S_ISDIR(st.st_mode)){
        puts("read dir");
        send_response_header(pdata->fd,200,st.st_size);
        send_response(pdata->fd,rpath,1);
    }
    else if(S_ISREG(st.st_mode)){
        //file
        puts("read file");
        send_response_header(pdata->fd,200,st.st_size);
        send_response(pdata->fd,rpath,0);
    }


    done:
    puts("send  and close......");
    free_event(pdata);




}
void read_data(ev_data * pdata){
    if(!pdata){
        puts("empty ptr");
        return;
    }
    if(!pdata->buffer){
        printf("empty ptr->buff , sock%d\n" , pdata->fd);
        return;
    }
    int fd = pdata->fd;
    int epfd = pdata->epfd;
    char * pbuffer = pdata->buffer;
    int left = BUFF_SIZE - pdata->nread;
    int len = 0,write_bytes = 0;

    while(left > 0){
        len = read(fd,pbuffer + pdata->nread,left);
        if(len < 0){
            if(errno == EAGAIN || errno == EWOULDBLOCK){
                puts("read all , done");
                break;
            }
            else{
                perror("read error :");
                free_event(pdata);
                return;
            }
        }
        else if( 0 == len){
            puts("closed");
            free_event(pdata);
            return;
        }
        left -= len;
        pdata->nread += len;
        if( pdata->nread > 4 ){
            if ( !is_http_get(pdata->buffer) ){
                free_event(pdata);
                return;
            }
        }


//        write_bytes = write_data(pdata);
//        if(write_bytes < 0){
//            break;
//        }
//        left += write_bytes;
    }
    http_request(pdata);
}
int write_data(ev_data * pdata){
    if(!pdata || !pdata->buffer){
        puts("empty ptr");
        return 0;
    }
    if(pdata->nread <= 0){
        printf("buffer is empty , ptr->nread:%d \n",pdata->nread);
        return 0;
    }
    int fd = pdata->fd;
    int epfd = pdata->epfd;
    char * pbuffer = pdata->buffer;
    int left = pdata->nread - pdata->start_write_index;

    int nwrite = 0 , write_bytes = 0;
    while(left > 0){
        nwrite = write(fd,pbuffer + pdata->start_write_index,left);
        if(nwrite <= 0){
            if(nwrite < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)){
                perror("sock send buff is full ");
                if(EPOLLOUT & pdata->events){
                    return write_bytes;
                }
                int new_events = pdata->events;
                new_events |= EPOLLOUT;
                update_event(new_events,pdata,pdata->epfd,EPOLL_CTL_MOD,pdata->fd);
                return write_bytes;
            }
            else{
                perror("write error , close socket ");
                free_event(pdata);
                return -1;
            }
        }
        left -= nwrite;
        pdata->start_write_index += nwrite;
        write_bytes += nwrite;
    }

    pdata->start_write_index = pdata->nread = 0;
    if(pdata->events & EPOLLOUT){
        update_event( EPOLLIN | EPOLLET,pdata,pdata->epfd,EPOLL_CTL_MOD,pdata->fd);
    }
    return write_bytes;
}


int main(int argc, char**argv)
{
    if(argc < 3){
        puts("epoll_httpserv port path");
        return 0;
    }
    sigset_t mask;
    sigfillset(&mask);
    sigdelset(&mask,SIGINT);
    pthread_sigmask(SIG_BLOCK,&mask,NULL);//sigwait / sigwaitinfo

    unsigned short port = atoi(argv[1]);

    if( chdir(argv[2]) != 0){
        perror("chdir failed :");
        return 0;
    }

    int epfd = epoll_create(EPOLL_SIZE);
    if(epfd < 0){
        perror("epoll_create ");
        return 0;
    }

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd < 0){
        perror("socket failed");
        return 0;
    }
    setnonblock(listenfd,1);
    struct sockaddr_in serv_addr;
    memset(&serv_addr ,0,sizeof(serv_addr));
    serv_addr.sin_port = htons(port);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    int on = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(int));
    if(bind(listenfd,(SA*)&serv_addr,sizeof(serv_addr)) < 0){
        perror("bind ");
        return 0;
    }
    if(listen(listenfd,BACKLOG) < 0){
        perror("listen ");
        return 0;
    }

    int events = EPOLLIN|EPOLLET;
    ev_data * ev = new_event(listenfd,epfd,events,0);
    if(!ev){
        puts("new failed");
        return 0;
    }
    update_event(events,ev,epfd,EPOLL_CTL_ADD,listenfd);
    struct epoll_event * epoll_arr = calloc(MAX_EVENTS, sizeof(struct epoll_event));
    if(!epoll_arr){
        puts("epoll arr failed");
        return 0;
    }
    printf("serv start port:%d , path:%s\n",port, argv[2]);
    int nready = 0,clt_fd = -1 , clt_events = EPOLLIN | EPOLLET;
    ev_data * clt_data = 0;
    while(1){
        nready = epoll_wait(epfd,epoll_arr,MAX_EVENTS,-1);
        if(nready < 0){
            perror("epoll_wait failed ");
            break;
        }
        for(int i = 0; i < nready ; ++i){
            if( (epoll_arr[i].events & EPOLLERR) || (epoll_arr[i].events & EPOLLHUP)){
                ev_data * pdata = epoll_arr[i].data.ptr;
                printf("index:%d ,occurs error:%d, fd:%d,epollfd:%d.\n",
                i,epoll_arr[i].events,pdata->fd,pdata->epfd);
                close(pdata->fd);
                free_event(pdata);
                continue;
            }
            if( epoll_arr[i].events & EPOLLIN){
                ev_data * pdata = epoll_arr[i].data.ptr;
                if(!pdata){
                    printf("EPOLLIN error, empty ptr\n");
                    continue;
                }
                if(listenfd == pdata->fd){
                    clt_fd = accept(listenfd,NULL,NULL);
                    if(clt_fd < 0){
                        perror("accept ");
                        continue;
                    }
                    setnonblock(clt_fd,1);
                    clt_data = new_event(clt_fd,epfd,clt_events,1);
                    if(!clt_data){
                        puts("new_event failed");
                        continue;
                    }
                    update_event(clt_events,clt_data,epfd,EPOLL_CTL_ADD,clt_fd);
                    printf("sock:%d accept ok !\n" , clt_fd);
                }
                else{
                    read_data(pdata);
                }
            }
            if(epoll_arr[i].events & EPOLLOUT){
                write_data(epoll_arr[i].data.ptr);
            }
        }
    }



    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值