一个简易的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;
}