以下注释均为测试,可忽略
绿色为文件,蓝色为目录
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 [$@]" @$(CC) -o $@ $^ $(LDFLAGS) @echo "Linking done..." %.o:%.c @echo "Compling [$<] to [$@]" @$(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="POST"> data1: <input type="text" name="data1"/><br/> data2: <input type="text" name="data2"/><br/> +: <input type="radio" name="op" value="ADD"><br/> -: <input type="radio" name="op" value="SUB"><br/> *: <input type="radio" name="op" value="MUL"><br/> /: <input type="radio" name="op" value="DIV"><br/> %: <input type="radio" name="op" value="MOD"><br/> <input type="submit" /><br/> </form> <hr/> <img src="20.jpg" alt="mantouheqi" dth="200" height="300"/> </body> </html>
cgi_math/math_cgi.cpp
#include <iostream> #include <string> #include <stdio.h> #include <errno.h> #include <vector> #include <stdlib.h> #include <string.h> #define _SIZE_ 2048 void get_args(std::vector<std::string> &vec,const char *method) { std::string query_string; std::string content_length; char ch='a'; int index=0; int ret=-1; char buf[_SIZE_]; memset(buf,'\0',sizeof(buf)); if(strcmp(method,"POST")==0){//POST std::string content_length=getenv("CONTENT_LENGTH"); int len=atoi(content_length.c_str()); memset(buf,'\0',sizeof(buf)); while(len){//&& index<sizeof(buf)-1){ ret=read(0,&ch,1); buf[index++]=ch; len--; } if(len!=0){ printf("error!\n"); //print_log(errno,__FUNCTION__,__LINE__); return; } }else{//GET query_string=getenv("QUERY_STRING"); // std::cout<<"math_cgi:query_string="<<query_string.c_str()<<std::endl; strcpy(buf,query_string.c_str()); // std::cout<<"math_cgi:buf="<<buf<<std::endl; index=query_string.size(); } buf[index]='\0'; // std::cout<<buf<<std::endl; char *end=buf+index; int incount=0; while(end!=buf){ if(*end == '='){ vec.push_back(end+1); //std::cout<<vec[incount++]<<std::endl; } if(*end == '&'){ *end='\0'; } end--; } } void echo_result(std::vector<std::string> &vec) { const char *op=vec[0].c_str(); int data1=atoi(vec[2].c_str()); int data2=atoi(vec[1].c_str()); int sum=0; char ch; if( strcmp("ADD",op)==0 ){ sum=data1+data2; ch='+'; }else if( strcmp("SUB",op)==0 ){ sum=data1-data2; ch='-'; }else if( strcmp("MUL",op)==0 ){ sum=data1*data2; ch='*'; }else if( strcmp("DIV",op)==0 ){ sum=data1/data2; ch='/'; }else if( strcmp("MOD",op)==0 ){ sum=data1%data2; ch='%'; }else{ return; } std::cout<<"HTTP/1.1 200 OK\r\n\r\n"; std::cout<<"<html>"<<std::endl; std::cout<<"<head>"<<std::endl; std::cout<<" "; std::cout<<"<h1>math_cgi "; std::cout<<ch <<"</h1>"<<std::endl; std::cout<<"</head>"<<std::endl; std::cout<<"<body>"<<std::endl; std::cout<<data1<<ch<<data2<<"="<<sum<<std::endl; std::cout<<"</body>"<<std::endl; std::cout<<"</html>"<<std::endl; } int main(int argc,char *argv[]) { std::string method=getenv("METHOD"); std::vector<std::string> vec; get_args(vec,method.c_str()); echo_result(vec); return 0; }
cgi_math/Makefile
math_cgi:math_cgi.cpp bigdata.cpp g++ -o $@ $^ -lstdc++ .PHONY:clean clean: rm -rf math_cgi .PHONY:output output:math_cgi cp -f math_cgi ../output/htdocs/cgi_bin/
运行:
make output;cd output
启动服务器
运行结果:
V2版本(即epoll模型),GET方法运行结果,并进行加法、乘法运算
《完》