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一个连接请求时,将对应的处理放到一个工作队列里,在队列里由多线程处理相应的回调函数。

  • 相关阅读:
    关于extern对变量的使用
    MediaPipe Android Archive
    MediaPipe框架结构
    bazel构建C++工程
    Ubuntu安装ss(终端+浏览器)
    Ubuntu下MediaPipe的环境配置
    Ubuntu使用frp进行内网穿透
    Ubuntu中ssh-server的安装与开机自启动
    Ubuntu系统挂载新硬盘的方法
    Ubuntu16.04下安装TensorFlow
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/3704573.html
Copyright © 2011-2022 走看看