以下注释均为测试,可忽略
绿色为文件,蓝色为目录
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方法运行结果,并进行加法、乘法运算
《完》
转载于:https://blog.51cto.com/lingdandan/1790849