без особого труда встроить в собственное приложение для обслуживания HTTP запросов.
Для использования этой возможности достаточно добавить в код следующее:
1. Подключить заголовочный файл <evhttp.h>:
#include <evhttp.h>
2. Инициализировать базу событийного движка:
event_base * serv_base = (event_base *)event_init();
3. Инициализировать HTTP сервер:
evhttp * http_server = evhttp_new(serv_base);
4. Указать, на каком сокете слушать подключения:
evhttp_accept_socket(http_server, server_sock);
5. Выставить callback'и на запросы. Можно добавлять на каждый URI свой обработчик:
evhttp_set_cb(http_server, "/news", on_request_news, NULL);
6. Выставить обработчик на остальные запросы:
evhttp_set_gencb(http_server, on_request, NULL);
7. Запустить цикл обработки запросов:
event_base_dispatch(serv_base);
Реализация HTTP сервера является потоко-безопасной (thread safe).
Есть еще один нюанс: настоятельно рекомендуется игнорировать сигнал SIGPIPE.
Делается это следующим вызовом:
signal(SIGPIPE, SIG_IGN);
Описание всех функция для работы с HTTP протоколом
можно найти в документации.
Вот работающий пример простого HTTP сервера, который на все запросы отдает динамическую
страничку с некоторой информацией о клиенте.
/* * \file: http_server.cpp * \description: Simple HTTP server * */ #include <errno.h> #include <event.h> #include <evhttp.h> #include <sys/socket.h> #include <sys/types.h> #include <signal.h> #include <netinet/in.h> #include <arpa/inet.h> #include <iostream> const short SERVER_BACKLOG = 128; const short BUF_LEN = 26; const char RESPONCE[BUF_LEN] = "<H1>Hello there</H1><BR/>"; const char * SERVER_NAME = "Simple HTTP Server"; void on_request(struct evhttp_request *, void *); int main(int argc, char **argv) { if (argc < 3) { std::cout << "Start as:" << std::endl << argv[0] << " host_address port" << std::endl; return 1; } int server_sock = socket(AF_INET, SOCK_STREAM, 0); if (server_sock == -1) { std::cout << "Error socket(): " << strerror(errno) << std::endl; return 1; } u_short port = atol(argv[2]); const char * host = argv[1]; sockaddr_in sa; int on = 1; sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = inet_addr(host); if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { std::cout << "Error setsockopt(): " << strerror(errno) << std::endl; return 1; } // Bind server socket to ip:port if (bind(server_sock, (const sockaddr*)&sa, sizeof(sa)) == -1) { std::cout << "Error bind(): " << strerror(errno) << " on: " << host << ":" << port << std::endl; return 1; } // Make server to listen if (listen(server_sock, SERVER_BACKLOG) == -1) { std::cout << "Error listen(): " << strerror(errno) << std::endl; return 1; } // Init events event_base * serv_base = (event_base *)event_init(); evhttp * http_server = evhttp_new(serv_base); // Ignore SIGPIPE signal(SIGPIPE, SIG_IGN); if (evhttp_accept_socket(http_server, server_sock) == -1) { std::cout << "Error evhttp_accept_socket(): " << strerror(errno) << std::endl; return 1; } // Set HTTP request callback evhttp_set_gencb(http_server, on_request, NULL); // Dispatch events event_base_dispatch(serv_base); return 0; } void on_request(struct evhttp_request * req, void * arg) { // Create responce buffer struct evbuffer *evb = evbuffer_new(); if (!evb) { return; } // Add heading text evbuffer_add_printf(evb, "<HTML><HEAD><TITLE>%s Page</TITLE></HEAD><BODY>\n", SERVER_NAME); // Add buffer evbuffer_add(evb, RESPONCE, BUF_LEN); // Add formatted text evbuffer_add_printf(evb, "Your request is <B>%s</B> from <B>%s</B>.<BR/>Your user agent is '%s'\n", req->uri, req->remote_host, evhttp_find_header(req->input_headers, "User-Agent")); // Add footer evbuffer_add_printf(evb, "</BODY></HTML>"); // Set HTTP headers evhttp_add_header(req->output_headers, "Server", SERVER_NAME); evhttp_add_header(req->output_headers, "Connection", "close"); // Send reply evhttp_send_reply(req, HTTP_OK, "OK", evb); // Free memory evbuffer_free(evb); }
Makefile для сборки:
SRCS=http_server.cpp LDFLAGS=-levent CXXFLAGS=-I/usr/local/include -L/usr/local/lib PROG=http_server all: c++ ${SRCS} -o ${PROG} ${LDFLAGS} ${CXXFLAGS} clean: rm -f ${PROG}
А вот результаты тестирования Apache Benchmark:
ab -c 100 -n 1000 "http://192.168.1.110:10000/test" Server Software: SimpleHTTPServer Server Hostname: 192.168.1.110 Server Port: 10000 Document Path: /test Document Length: 199 bytes Concurrency Level: 100 Time taken for tests: 0.185 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 308000 bytes HTML transferred: 199000 bytes Requests per second: 5393.74 [#/sec] (mean) Time per request: 18.540 [ms] (mean) Time per request: 0.185 [ms] (mean, across all concurrent requests) Transfer rate: 1622.34 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 5 2.8 5 16 Processing: 5 13 3.9 13 23 Waiting: 2 10 3.9 9 22 Total: 11 18 3.4 18 27 Percentage of the requests served within a certain time (ms) 50% 18 66% 19 75% 20 80% 21 90% 22 95% 24 98% 25 99% 26 100% 27 (longest request)
Сервер был запущен локально на моем ноутбуке, на сервере результаты конечно же получше.
Но для сотни параллельных запросов – это неплохо.
5 комментариев:
Круть. А все ж интересно, это добро под WinMobile собирается? Было бы неплохо HTTP-сервер на мобилке настроить :) Я даже у кого-то такое видел. Можно было через Интернет на мобилку зайти и там чото типа новоестей было или какое-то простенькое подобие твиттера.
Скомпилировал. Запускаю. Вешаю на localhost 32. Ни один http-клиент не может к нему подсоединиться, говорят 'connection refued' ... Печально!(( Хотя, вот как здесь http://www.linuxhowtos.org/C_C++/socket.htm всё пашет, то есть проблем с сетями у меня нет.
Всё же удалось заставить работать. Не знаю, почему у Вас сокет не открывался, но у меня сервер отдаёт ответы. У Вас ещё ошибка в описании того, как добавлять события: пропущен слеш перед news:
evhttp_set_cb(http_server, "/news", on_request_news, NULL);
Без него выполняется обработчик по-умолчанию.
Спасибо, поправлю.
Отправить комментарий