среда, 20 мая 2009 г.

Вебинар от QNX SS "Exactly When Do You Need Realtime?"


21 мая, компания QNX Software Systems проведет бесплатный онлайн-семинар, который должен помочь при выборе операционной системы для встраиваемых систем.

Продолжительность: 1 час, включая вопросы.

Все ли встраиваемые проекты нуждаются в ОСРВ?.. Завтра узнаем.



пятница, 8 мая 2009 г.

К проекту прилагается отличная документация в формате С

Отличная статья про то, как хорошо и полезно уметь читать и читать исходный код:
http://gaperton.livejournal.com/32772.html

Когда я только начинал программировать, у меня совсем не было интернета, а в моем городе не было вменяемой литературы, поэтому я привык пользоваться справкой.
Читая сопроводительные тексты к среде разработки и к библиотекам, наткнулся на следующее высказывание: "Чтобы научиться программировать – нужно читать очень много кода, а писать еще больше".

Документация-документацией, но все же, не помешает (а часто и поможет) знание того, как это работает "под капотом".

четверг, 7 мая 2009 г.

Написание своего HTTP сервера с использованием libevent

Библиотека libevent содержит в себе простейший асинхронный HTTP сервер, который можно
без особого труда встроить в собственное приложение для обслуживания 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)


Сервер был запущен локально на моем ноутбуке, на сервере результаты конечно же получше.
Но для сотни параллельных запросов – это неплохо.