1. 程式人生 > >libevent學習---簡單http server實現

libevent學習---簡單http server實現

libevent中有evhttp實現了http server,看了一下,4000多行程式碼。

其實實現一個http server的步驟很簡單,無非就是業務上的內容,至於優化和效能都是寫程式碼及架構設計模式的問題,這部分我太菜了就不說了。

簡單說下這篇博文的思路:
1.作為一個簡單的HTTP server,在read callback裡面,直接write http request到標準輸出。
2.另外在read callback裡面,向libevent中提供的evbuffer 裡面構造響應,http響應由如下內容組成:

(a) 響應行:狀態 相應碼等
(b) 響應頭:這裡我就寫了Server Content-Type Content-Length等
(c) 響應報文:簡單的h5程式碼,用兩個\r\n和response head隔開

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>

#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h> #include <netdb.h> void errorcb(struct bufferevent*bev,short events,void *ptr) { printf("in errorcb\r\n"); if(events&BEV_EVENT_CONNECTED) { printf("ok connected\r\n"); } else if(events&(BEV_EVENT_ERROR|BEV_EVENT_EOF)) { struct
event_base *base=ptr; printf("closing... \r\n"); bufferevent_free(bev); event_base_loopexit(base,NULL); } } void readcb(struct bufferevent*bev,void *arg ) { printf("in readcb\r\n"); struct evbuffer *input; struct evbuffer *output; char *request_line; size_t len; input = bufferevent_get_input(bev); output = bufferevent_get_output(bev); size_t input_len = evbuffer_get_length(input); while(1) { request_line = evbuffer_readln(input,&len,EVBUFFER_EOL_CRLF); if(NULL==request_line) { //printf("evbuffer_readln error\r\n"); free(request_line); break; } else { char request_buf[256]; snprintf(request_buf,len+5,"> %s\r\n",request_line); write(1,request_buf,strlen(request_buf)); } } //構造響應報文 char content[]="<html><head><title>This is title</title></head><body><h1>Hello</h1></body></html>"; char tmp[]="HTTP/1.1 200 OK\r\nServer: ZhangXiao\r\nContent-Length: %d\r\nContent-Type: text/html\r\n\r\n%s"; int contentlen=strlen(content); char response[256]; snprintf(response,256,tmp,contentlen,content); char *p = response; evbuffer_add(output,p,strlen(response)); free(request_line); } void do_accept(evutil_socket_t listener,short event,void*arg) { printf("in accept\r\n"); struct event_base *base = (struct event_base *)arg; struct sockaddr_in ss; socklen_t slen = sizeof(ss); int fd = accept(listener,(struct sockaddr *)&ss,&slen); assert(fd>0); struct bufferevent *bev; evutil_make_socket_nonblocking(fd); //使用buffer_socket_new建立一個struct bufferevent*,關聯fd //託管給event_base,BEV_OPT_CLOSE_ON_FREE表示釋放bufferevent的時候 //關閉底層套接字等... bev = bufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE); //設定對應的回撥函式 bufferevent_setcb(bev,readcb,NULL,errorcb,base); bufferevent_enable(bev,EV_READ|EV_WRITE);//event_add } int main(int argc,char **argv) { evutil_socket_t listener; struct sockaddr_in sin; struct event_base *base; struct event* listener_event; base = event_base_new(); assert(NULL!=base); sin.sin_family = AF_INET; sin.sin_addr.s_addr = 0; sin.sin_port=htons(1025); listener = socket(AF_INET,SOCK_STREAM,0); assert(listener>0); evutil_make_socket_nonblocking(listener);//fcntl的封裝 if(bind(listener,(struct sockaddr*)&sin,sizeof(sin))<0) { perror("bind"); return -1; } if(listen(listener,16)<0) { perror("listen"); return -1; } listener_event=event_new(base,listener,EV_READ|EV_PERSIST,do_accept,(void*)base); event_add(listener_event,NULL);//add to pending event lists event_base_dispatch(base); return 0; }