zoukankan      html  css  js  c++  java
  • 基于Libevent的HTTP Server

    简单的Http Server

    使用Libevent内置的http相关接口,可以很容易的构建一个Http Server,一个简单的Http Server如下:

    #include <event2/event.h>
    #include <event2/buffer.h>
    #include <event2/http.h>
    #include <Winsock2.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int init_win_socket()
    {
        WSADATA wsaData;
        if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0) 
        {
            return -1;
        }
        return 0;
    }
    
    void generic_handler(struct evhttp_request *req, void *arg)
    {
        struct evbuffer *buf = evbuffer_new();
        if(!buf)
        {
            puts("failed to create response buffer 
    ");
            return;
        }
    
        evbuffer_add_printf(buf, "Server Responsed. Requested: %s
    ", evhttp_request_get_uri(req));
        evhttp_send_reply(req, HTTP_OK, "OK", buf);
        evbuffer_free(buf);
    }
    
    int main(int argc, char* argv[])
    {
    #ifdef WIN32
        init_win_socket();
    #endif
    
        short          http_port = 8081;
        char          *http_addr = "127.0.0.1";
    
        struct event_base * base = event_base_new();
    
        struct evhttp * http_server = evhttp_new(base);
        if(!http_server)
        {
            return -1;
        }
    
        int ret = evhttp_bind_socket(http_server,http_addr,http_port);
        if(ret!=0)
        {
            return -1;
        }
    
        evhttp_set_gencb(http_server, generic_handler, NULL);
    
        printf("http server start OK! 
    ");
    
        event_base_dispatch(base);
    
        evhttp_free(http_server);
    
        WSACleanup();
        return 0;
    }

    通过Libevent的接口构建一个Http Server的过程如下:

    (1)初始化:在event_base上新建一个evhttp,将这个evhttp绑定到监听的IP和端口号。

    (2)设置Http回调函数:使用evhttp_set_gencb设置Http Server的处理请求的回调函数。

    (3)启动Http Server:等待请求进入事件循环。

    在Http Server中使用定时器提供更新服务

    #include <event2/event.h>
    #include <event2/buffer.h>
    #include <event2/http.h>
    #include <sys/stat.h>
    #include <Winsock2.h>
    #include <assert.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    #define DEFAULT_FILE "F:\Libevent\LibeventTest\Debug\sample.txt"
    
    char *filedata;
    time_t lasttime = 0;
    char filename[80];
    int counter = 0;
    
    struct event *loadfile_event;
    struct timeval tv;
    
    void read_file()
    {
        unsigned long size = 0;
        char *data;
        struct stat buf;
    
        if(stat(filename,&buf)<0)
        {
            printf("Read file error! 
    ");
            return;
        }
    
        if (buf.st_mtime > lasttime)
        {
            if (counter++)
                fprintf(stderr,"Reloading file: %s",filename);
            else
                fprintf(stderr,"Loading file: %s",filename);
    
            FILE *f = fopen(filename, "rb");
            if (f == NULL)
            {
                fprintf(stderr,"Couldn't open file
    ");
                return;
            }
    
            size = buf.st_size;
            filedata = (char *)malloc(size+1);
            memset(filedata,0,size+1);
            fread(filedata, sizeof(char), size, f);
            fclose(f);
    
            fprintf(stderr," (%d bytes)
    ",size);
            lasttime = buf.st_mtime;
        }
    }
    
    void read_file_timer_cb(evutil_socket_t listener, short event, void *arg)
    {
        if (!evtimer_pending(loadfile_event, NULL)) 
        {
            event_del(loadfile_event);
            evtimer_add(loadfile_event, &tv);
        }
    
        read_file();
    }
    
    void load_file(struct event_base * base)
    {
        tv.tv_sec = 5;
        tv.tv_usec = 0;
    
        //loadfile_event = malloc(sizeof(struct event));
        loadfile_event = evtimer_new(base,read_file_timer_cb,NULL);
    
        //evtimer_set(loadfile_event,load_file,loadfile_event);
        evtimer_add(loadfile_event,&tv);
    }
    
    void generic_handler(struct evhttp_request *req, void *arg)
    {
        struct evbuffer *buf = evbuffer_new();
        if(!buf)
        {
            puts("failed to create response buffer 
    ");
            return;
        }
        evbuffer_add_printf(buf,"%s",filedata);
        evhttp_send_reply(req, HTTP_OK, "OK", buf);
        evbuffer_free(buf);
    }
    
    int init_win_socket()
    {
        WSADATA wsaData;
        if(WSAStartup(MAKEWORD(2,2) , &wsaData) != 0) 
        {
            return -1;
        }
        return 0;
    }
    
    int main(int argc, char* argv[])
    {
    #ifdef WIN32
        init_win_socket();
    #endif
    
        short          http_port = 8081;
        char          *http_addr = "127.0.0.1";
    
        if (argc > 1)
        {
            strcpy(filename,argv[1]);
            printf("Using %s
    ",filename);
        }
        else
        {
            strcpy(filename,DEFAULT_FILE);
        }
    
        struct event_base * base = event_base_new();
    
        struct evhttp * http_server = evhttp_new(base);
        if(!http_server)
        {
            return -1;
        }
    
        int ret = evhttp_bind_socket(http_server,http_addr,http_port);
        if(ret!=0)
        {
            return -1;
        }
    
        evhttp_set_gencb(http_server, generic_handler, NULL);
    
        read_file();
    
        load_file(base);
    
        printf("http server start OK! 
    ");
    
        event_base_dispatch(base);
    
        evhttp_free(http_server);
    
        WSACleanup();
        return 0;
    }

    在这个Http Server中提供了一个每5秒触发一次的定时器,读取一个文件,如果这个文件被更新过,则读取更新后的内容。

    当访问这个Http Server时,提供这个文件中最新的内容。

    多线程的Http Server

    在上面的Http Server中,处理Http请求的回调函数generic_handler和定时器读取文件的回调函数read_file_timer_cb都在同一个event_base的dispatch中,并且都在同一个进程中,使用多线程可以改善程序的性能,下面是一个来自网络的多线程Http Server:

    #include <event.h>
    #include <evhttp.h>
    #include <pthread.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
     
    int httpserver_bindsocket(int port, int backlog);
    int httpserver_start(int port, int nthreads, int backlog);
    void* httpserver_Dispatch(void *arg);
    void httpserver_GenericHandler(struct evhttp_request *req, void *arg);
    void httpserver_ProcessRequest(struct evhttp_request *req);
     
    int httpserver_bindsocket(int port, int backlog) {
      int r;
      int nfd;
      nfd = socket(AF_INET, SOCK_STREAM, 0);
      if (nfd < 0) return -1;
     
      int one = 1;
      r = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
     
      struct sockaddr_in addr;
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = INADDR_ANY;
      addr.sin_port = htons(port);
     
      r = bind(nfd, (struct sockaddr*)&addr, sizeof(addr));
      if (r < 0) return -1;
      r = listen(nfd, backlog);
      if (r < 0) return -1;
     
      int flags;
      if ((flags = fcntl(nfd, F_GETFL, 0)) < 0
          || fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
        return -1;
     
      return nfd;
    }
     
    int httpserver_start(int port, int nthreads, int backlog) {
      int r, i;
      int nfd = httpserver_bindsocket(port, backlog);
      if (nfd < 0) return -1;
      pthread_t ths[nthreads];
      for (i = 0; i < nthreads; i++) {
        struct event_base *base = event_init();
        if (base == NULL) return -1;
        struct evhttp *httpd = evhttp_new(base);
        if (httpd == NULL) return -1;
        r = evhttp_accept_socket(httpd, nfd);
        if (r != 0) return -1;
        evhttp_set_gencb(httpd, httpserver_GenericHandler, NULL);
        r = pthread_create(&ths[i], NULL, httpserver_Dispatch, base);
        if (r != 0) return -1;
      }
      for (i = 0; i < nthreads; i++) {
        pthread_join(ths[i], NULL);
      }
    }
     
    void* httpserver_Dispatch(void *arg) {
      event_base_dispatch((struct event_base*)arg);
      return NULL;
    }
     
    void httpserver_GenericHandler(struct evhttp_request *req, void *arg) {
          httpserver_ProcessRequest(req);
    }
     
    void httpserver_ProcessRequest(struct evhttp_request *req) {
        struct evbuffer *buf = evbuffer_new();
        if (buf == NULL) return;
        
        //here comes the magic
    }
     
    int main(void) {
        httpserver_start(80, 10, 10240);
    }

    上面的代码基于Libevent 1.X版本的,不过很容易很看懂:在一个监听socket上创建了多个event_base实例和evhttp实例,在不同的线程中调度不同的event_base,继而可以在不同的线程中处理http请求。

    这里还有一个基于Libevent的多线程Http Server:https://sourceforge.net/projects/libevent-thread/,看源代码处理的过程和上面类似,只是每次在监听的socket上accept一个连接请求时,将对应的处理放到一个工作队列里,在队列里由多线程处理相应的回调函数。

  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    block的是发送信号的线程,又不是处理槽函数的线程
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/3704573.html
Copyright © 2011-2022 走看看