HTTP服务器(多线程版本epoll版本)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HTTP服务器(多线程版本epoll版本)相关的知识,希望对你有一定的参考价值。

以下注释均为测试,可忽略

绿色为文件蓝色为目录

main.c

#include "httpd.h"
                                                                                                                                      
void* thread_run(void *arg)
{
    int sock=(int)arg;
    accept_request(sock);
    printf("one accept finshed...\n");
}
 
int main(int argc,char *argv[])
{
    if(argc!=3){
        usage(argv[0]);
        return 1;
    }   
 
    int port=atoi(argv[2]);
    char *ip=argv[1];
    int listen_sock = bind_sock(port,ip);
 
#ifdef _V1_
    struct sockaddr_in client;
    socklen_t client_len=sizeof(client);
     
    int done=0;
    while(!done){
        int sock=accept(listen_sock,(struct sockaddr*)&client,&client_len);
        if(sock<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return 1;
        }
        printf("get a new connection:%s,%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        pthread_t tid;
        if(pthread_create(&tid,NULL,thread_run,(void *)sock)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return 1;
        }
        if(pthread_detach(tid)<0){                                                                                                    
            print_log(errno,__FUNCTION__,__LINE__);
            return 1;
        }
    }
#elif _V2_
    server_handler(listen_sock);
#else
    printf("no more version...\n");
    
#endif
                                                                                                                                      
    return 0;
}

httpd.h

#include <stdio.h>
#include <stdlib.h>                                                                                                                   
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <sys/epoll.h>
 
#define __BLOCK__ 6
#define _SIZE_ 1024
#define _DEF_html_ "index.html"
#define _MAX_FD_NUM_ 64
 
typedef struct buf_ptr
{  
    int sock;
    int fd;
    int fd_size;
    int pipefd;
    char ptr[4096];
}buf_t,*buf_p;                                                                                                                        
 
void usage(const char *proc);
void print_log(int err,const char *func,int line);
int bind_sock(int _port,const char *ip);
buf_p accept_request(int sock);
int server_handler(int listen_sock);

httpd.c

#include "httpd.h"
 
void usage(const char *proc)
{                                                                                                                                     
    printf("usage:%s [ip] [port]\n",proc);
}
 
void print_log(int err,const char *func,int line)
{
    printf("%s****** %d :%s\n",func,line,strerror(err));
}
 
static int set_non_block(int fd) 
{
    int OLD_FL=fcntl(fd,F_GETFL);
    if(OLD_FL<0){
        perror("fcntl");
        return -1; 
    }   
    if(fcntl(fd,F_SETFL,OLD_FL|O_NONBLOCK)<0){
        perror("fcntl");
        return -2; 
    }   
    return 0;
}
   
int bind_sock(int _port,const char *ip)
{                                                                                                                                     
    int listen_sock=socket(AF_INET,SOCK_STREAM,0);
    if(listen_sock<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return 2;
    }
   
    int opt=1;
    setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
   
    struct sockaddr_in local;
    local.sin_family=AF_INET;
    local.sin_port=htons(_port);
    local.sin_addr.s_addr=inet_addr(ip);
    socklen_t len=sizeof(local);
    if(bind(listen_sock,(struct sockaddr*)&local,len)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return 2;
    }
    if(listen(listen_sock,__BLOCK__)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return 2;
    }
    return listen_sock;
}   

int server_handler(int listen_sock)
{   
    //创建epoll描述符
    int epoll_fd=epoll_create(256);
    if(epoll_fd<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return 2;
    }
    
    struct epoll_event ev;
    ev.events=EPOLLIN|EPOLLET;
    ev.data.fd=listen_sock;
    //添加监听描述符事件
    if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&ev)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return 2;
    }                                                                                                                                 
    
    struct epoll_event ev_set[_MAX_FD_NUM_];
    int timeout=5000;
//    int timeout=-1;

    char buf[1024];                                                                                                                   
    int ready_num=-1;
    int i=0;
    int done=0;
    while(!done){
        //等待事件的发生
        if((ready_num=epoll_wait(epoll_fd,ev_set,_MAX_FD_NUM_,timeout))<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return 2;
        }else if(ready_num==0){
            printf("epoll timeout...\n");
        }else{
            for(i=0;i<ready_num;++i){
                //printf("EPOLLIN=%d,ready_num=%d\n",EPOLLIN,ready_num);
                if(ev_set[i].data.fd==listen_sock && (ev_set[i].events&EPOLLIN)){
                    struct sockaddr_in client;
                    socklen_t len=sizeof(client);
                    //连接请求
                    int sock=accept(listen_sock,(struct sockaddr*)&client,&len);
                    if(sock<0){
                        print_log(errno,__FUNCTION__,__LINE__);
                        return 2;
                    }
                    //设置套接字为非阻塞
                    if(set_non_block(sock)<0){
                        printf("set noblock failed...\n");
                        return 2;
                    }
    
                    ev.data.fd=sock;
                    ev.events=EPOLLIN|EPOLLET;
                    //添加套接字到epoll_fd
                    if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,sock,&ev)<0){
                        print_log(errno,__FUNCTION__,__LINE__);                                                                       
                        return 2;
                    }
                    printf("get a new connection:%s,%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
                 }else if(ev_set[i].events&(EPOLLIN|EPOLLET)){
                    int sock=ev_set[i].data.fd;
                    buf_p mem=(buf_p)malloc(sizeof(buf_t));
                    memset(mem,‘\0‘,sizeof(buf_t));
                    if(!mem){
                        perror("malloc");
                        continue;
                    }
                    mem->sock=sock;
   
                    buf_p tmp_buf=accept_request(sock);
                    mem->fd=tmp_buf->fd;
                    mem->fd_size=tmp_buf->fd_size;
                    mem->pipefd=tmp_buf->pipefd;
                                                                                                                                      
                    free(tmp_buf);
   
                    int index=0;
                    int ret=0;
                    if((mem->pipefd)>0){
                        while(ret=read(mem->pipefd,(mem->ptr)+index,sizeof(mem->ptr)-1-index)){//notes! there only use read  ,not recv 
                            if(ret<0){
                                print_log(errno,__FUNCTION__,__LINE__);
                                return;                                                                                               
                            }
                            index+=ret;
                        }
                        
                        //printf("index=%d\n",index);
                        //mem->ptr[index]=‘\0‘;                                                                                       
                        //printf("mem->ptr=%s\n",mem->ptr);
                    }
                    ev.events=EPOLLOUT|EPOLLET;                                                                                       
                    ev.data.ptr=mem;
                    epoll_ctl(epoll_fd,EPOLL_CTL_MOD,mem->sock,&ev);
                    ev_set[i].data.ptr=(void *)mem;//can‘t operator to ev_set[]***************
                }else if((ev_set[i].events&(EPOLLOUT)|EPOLLET)){
                    buf_p mem=(buf_p)ev_set[i].data.ptr;
                    int sock=mem->sock;
                    int fd=mem->fd;
                    int fd_size=mem->fd_size;
                    int pipefd=mem->pipefd;
                    char *buf=mem->ptr;
                    
                    if(pipefd<0){//cgi==0
                        char *status_line="HTTP/1.1 200 OK\r\n\r\n";
                        if(send(sock,status_line,strlen(status_line),0)<0){
                            print_log(errno,__FUNCTION__,__LINE__);
                            close(fd);
                            return;
                        }
                        
                        if(sendfile(sock,fd,NULL,fd_size)<0){
                            print_log(errno,__FUNCTION__,__LINE__);                                                                   
                        }
                    }else{//cgi==1
                        send(sock,buf,strlen(buf),0);
                        
                        close(pipefd);
                    }
                    printf("send file success!\n");
                    
                    epoll_ctl(epoll_fd,EPOLL_CTL_DEL,sock,NULL);
                    close(sock);
                    close(fd);
                    free(mem);
                }
            }
        }
    }
}   

static int get_line(int sock,char *buf,int size)
{   
    assert(buf);
    int index=0;
    char ch=‘\0‘;
    while(index<size-1 && ch!=‘\n‘){
        if(recv(sock,&ch,1,0)>0){//***************************************block
            if(ch==‘\r‘){
                if(recv(sock,&ch,1,MSG_PEEK)<0){
                    print_log(errno,__FUNCTION__,__LINE__);
                    return -1;
                }else{
                    if(ch==‘\n‘){
                        recv(sock,&ch,1,0);
                    }
                }
            }
            buf[index++]=ch;                                                                                                          
        }else{
            ch=‘\n‘;
        }
    }
    buf[index]=‘\0‘;
    return index;
}
   
static void handler_clear(int sock)                                                                                                   
{
    char buf[_SIZE_];
    while(get_line(sock,buf,sizeof(buf)) && strcmp(buf,"\n")){
        //printf("%s",buf);
    }
}  
   
static void echo_html(int sock,int fd,off_t size)
{  
//  printf("path=%s\n",path);
#ifdef _V1_
    char *status_line="HTTP/1.1 200 OK\r\n\r\n";
    if(send(sock,status_line,strlen(status_line),0)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        close(fd);
        return;
    }
    
    if(sendfile(sock,fd,NULL,size)<0){
        print_log(errno,__FUNCTION__,__LINE__);
    }
    printf("send file success!\n");
    close(sock);
    close(fd);
#endif
}   
    
static int exe_cgi(int sock,const char *path,const char *query_string)
{   
    char buf[_SIZE_];
    int ret=-1;
    int content_len=-1;
    char *len=NULL;                                                                                                                   
    int firstfd[2];
    int secondfd[2];
    if(strcmp(query_string,"\0")==0){//POST
        if(setenv("METHOD","POST",1)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return;
        }
        printf("export METHOD success!\n");
        while(ret=get_line(sock,buf,sizeof(buf)) && strcmp(buf,"\n")!=0){
            //printf("buf===============%s",buf);
            if(strncasecmp(buf,"Content-Length: ",15)==0){//length is 15 include a space
                buf[ret-1]=‘\0‘;
                len=buf+15;
                content_len=atoi(len);
                if(setenv("CONTENT_LENGTH",len,1)<0){//set env_var
                    print_log(errno,__FUNCTION__,__LINE__);
                    return;
                }                                                                                                                     
                printf("export CONTENT_LENGTH success!\n");
            }
        }
        printf("POST success\n");
    }else{//GET
        if(setenv("METHOD","GET",1)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return;
        }
        printf("export METHOD success!\n");
        if(setenv("QUERY_STRING",query_string,1)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return;
        }
        printf("export QUERY_STRING success!\n");
        handler_clear(sock);
        //handler_clear(sock);
        printf("GET success\n");
    }   
        
    if(pipe(firstfd)<0){                                                                                                              
        print_log(errno,__FUNCTION__,__LINE__);
        return;
    }   
    if(pipe(secondfd)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return;
    }   
    pid_t id=fork();
    if(id<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return;
    }   
    else if(id==0){
        close(firstfd[1]);
        close(secondfd[0]);
        if(dup2(firstfd[0],0)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return;                                                                                                                   
        }
        if(dup2(secondfd[1],1)<0){
            print_log(errno,__FUNCTION__,__LINE__);
            return;
        }
        close(secondfd[1]);
        close(firstfd[0]);
        execl(path,"abcdefg",NULL);
    }else{
        close(firstfd[0]);
        close(secondfd[1]);
        char ch=‘a‘;
        if(strcmp(query_string,"")==0){//POST
            while(content_len){
                recv(sock,&ch,1,0);
                write(firstfd[1],&ch,1);
                content_len--;
            }
        }
        int ret=-1;
#ifdef _V1_
        while(ret=read(secondfd[0],&ch,1)){//notes! there only use read  ,not recv 
            if(ret<0){
                print_log(errno,__FUNCTION__,__LINE__);                                                                               
                return;
            }
        //  printf("ret=%d,ch=%c\n",ret,ch);
            send(sock,&ch,1,0);
        }
        
        close(sock);
        close(secondfd[0]);
        close(firstfd[1]);
        return 0;
#elif _V2_
        return secondfd[0];
#endif  
    }   
}       
        
buf_p accept_request(int sock)
{       
    char buf[_SIZE_];
#ifdef _DEBUG_
    int ret=-1;
    while(ret=get_line(sock,buf,sizeof(buf))){
        printf("%d:%s",ret,buf);
        if(strcmp(buf,"\n")==0){
            break;
        }
    }   
#else   
    int ret=get_line(sock,buf,sizeof(buf));
    if(ret<0){
        print_log(errno,__FUNCTION__,__LINE__);
        return;
    }   
    char *method=buf;
    char *url=buf;
    char *query_string=buf;                                                                                                           
    char *protocol=buf;
        
    char path[_SIZE_*2];
    memset(path,‘\0‘,sizeof(path));
    int cgi=0;
    //if method is not both GET or POST,exit!
    if( strncasecmp(method,"GET",3)!=0 && strncasecmp(method,"POST",4)!=0 ){
        printf("method:%s\n",method);
        printf("method error!\n");
        return;
    }   
    //method is POST,running with cgi
    if( strncasecmp(method,"POST",4)==0 ){
        method[4]=‘\0‘;
        url+=5;
        cgi=1;
    }else{
        method[3]=‘\0‘;
        url+=4;
    }   
    query_string=url;                                                                                                                 
    while(*query_string!=‘?‘ && *query_string!=‘ ‘){
        query_string++;
    }   
    if(*query_string==‘?‘){
        *query_string=‘\0‘;
        query_string++;
        protocol=query_string;
        while(*protocol!=‘ ‘){
            protocol++;
        }
        *protocol=‘\0‘;
        protocol++;
        cgi=1;
    }else{
        *query_string=‘\0‘;
        protocol=query_string+1;
    }  
    char *tmp=protocol;
    while(*tmp!=‘\r‘ && *tmp!=‘\n‘){
        tmp++;
    }                                                                                                                                 
    *tmp=‘\0‘;
     
    if(strcmp(url,"/")==0){
        strcpy(path,"htdocs/");
        strcat(path,_DEF_HTML_);
    }else{
    //  strcpy(path,url+1);
        
        strcpy(path,"htdocs");
        strcat(path,url);
    }   
        
    printf("method=%s\n",method);
    printf("path=%s\n",path);
    printf("query_string=%s\n",query_string);
    printf("protocol=%s\n",protocol);
    printf("cgi=%d\n",cgi);
    printf("path=%s\n",path);                                                                                                         
        
    int fd=open(path,O_RDONLY);
    if(fd<0){//file not exist
        print_log(errno,__FUNCTION__,__LINE__);
        return;
    }   
    
    struct stat fbuf;
    if(fstat(fd,&fbuf)<0){
        print_log(errno,__FUNCTION__,__LINE__);
        close(fd);
        return;
    }   
        
    if(S_IFDIR&(fbuf.st_mode)){//dir
        printf("file is invalid!\n");
        return;
    }else if((S_IXUSR&(fbuf.st_mode)) || (S_IXGRP&(fbuf.st_mode)) || (S_IXOTH&(fbuf.st_mode))){//2binary
        cgi=1;
    }   
    int size=fbuf.st_size;                                                                                                            
        
    int pipefd=-1;
    if(cgi==0){//GET
        handler_clear(sock);
        echo_html(sock,fd,size);
    }else{//GET or POST
        pipefd=exe_cgi(sock,path,query_string);
    }   
    buf_p tmp_buf=(buf_p)malloc(sizeof(buf_t));                                                                                       
    tmp_buf->fd=fd;
    tmp_buf->fd_size=size;
    tmp_buf->pipefd=pipefd;
    return tmp_buf;
#endif  
}

Makefile

ROOT_PATH=$(shell pwd)
LDFLAGS=-lpthread
FLAGS=#-D_DEBUG_#-g
                                                                                                                                      
CC=gcc
BIN=httpd
SRC=$(shell ls *.c)
OBJ=$(SRC:.c=.o)
 
CGI_BIN=cgi_bin
 
.PHONY:all
all:$(BIN) cgi 
$(BIN):$(OBJ)
    @echo "Linking [$^] to [[email protected]]"
    @$(CC) -o [email protected] $^  $(LDFLAGS)
    @echo "Linking done..."
%.o:%.c
    @echo "Compling [$<] to [[email protected]]"
    @$(CC) -c $< -D_V2_ $(FLAGS) 
    @echo "Compling done..."
 
.PHONY:clean
clean:
    @rm -rf *.o $(BIN) output
    @for name in `echo $(CGI_BIN)`;    do        cd $$name;\                                                                                                                   
        make clean;        cd -;    done
    
.PHONY:cgi
cgi:
    @for name in `echo $(CGI_BIN)`;    do        cd $$name;        make;        cd -;    done
.PHONY:output
output:$(BIN) cgi
    @mkdir -p output/log
    @cp -rf htdocs output/
    @mkdir -p output/htdocs/cgi_bin
    @cp -f httpd output/
    @cp -f start.sh output/
    @cp -rf conf output
    @for name in `echo $(CGI_BIN)`;    do        cd $$name;\                                                                                                                   
        make output;        cd -;    done


htdocs/index.html

<html>
    <head>
        <h1>hello chehlling</h1>
    </head>
    
    <body>
        <hr/>
        <form action="cgi_bin/math_cgi" method="

以上是关于HTTP服务器(多线程版本epoll版本)的主要内容,如果未能解决你的问题,请参考以下文章

网络 http服务器-v2-epoll版本

Http多线程版本

网络 http服务器-v1-多线程版本

多人聊天室 (多线程 || epoll)

《深入理解计算机系统》Tiny服务器4——epoll类型IO复用版Tiny

epoll中et+多线程模式中很重要的EPOLL_ONESHOT实验