libuv — библиотека для асинхронного I/O

libuvПроцессе изучения Javascript и Node я как системный программист не мог не поинтересоваться его внутренним устройством. Одной из интересных находок стала библиотека libuv.

libuv

libuv — кроссплатформенная библиотека асинхронного ввода-вывода(I/O), разрабатываемая для Node.JS. Библиотека «навязывает» асинхронный, событийно-ориентированный стиль программирования(Node же). Эта библиотека как и libevent2 использует наиболее эффективный из доступных в системе способов асинхронной работы с сокетами(epoll, kqueue, event ports,  IOCP), но в отличии от libevent, которая использует буферизированный ввод-вывод для операций с файловой системой, libuv для работы с файловой системой применяет синхронные вызовы в пуле потоков. Помимо этого библиотека предоставляет кроссплатформенные примитивы для работы с потоками(threads).

 

Обработчики и запросы(Handles and requests).

Вместе с циклом обработки событий(event-loop) libuv предоставляет 2 абстракции для работы: обработчики и запросы.

Обработчики представляют собой долгоживущие объекты способные выполнять определенные операции пока активны. Например: обработчик соединения TCP-сервера, который вызывается каждый раз когда появляется новое соединение.

Запросы представляют собой краткосрочные(как правило) операции. Эти операции могут быть выполнены над обработчиком, например, запрос на запись используется для того что бы записать данные в обработчике.

Event loop

I/O(или event) loop(я не подобрал хорошего перевода для этого термина, а перевод цикл событий мне не нравится) центральная часть библиотеки. Он устанавливает контент для всех I/O операций и они должны находится в одном потоке. Одно приложение может иметь несколько event loop’в, но каждый должен быть запущен в отдельном потоке. Event loop библиотеки libuv не потокобезопасный(thread-safe).

 

Установка библиотеки

В репозитории Ubuntu 15.04 находится версия 0.10, в то время как уже существует версия 1.9.0(на 16.04.2016). Скачиваем последний стабильную версию отсюда: http://dist.libuv.org/dist/

и распаковываем куда удобно и переходим в каталог с исходниками. Теперь установим все необходимое для сборки:

sudo apt-get install libtool automake checkinstall

checkinstall нам нужен что бы из исходников собрать deb-пакет и установить его, потому что установка исходников по средством make install я считаю плохой затеей. Дальше:

sh autogen.sh
 
./configure
 
make
 
sudo checkinstall
 
sudo dpkg -i libuv_1.9.0-1_amd64.deb

Все теперь у нас собрана и установлена свежая версия библиотеки(если архитектура вашей операционной системы отличается, то имя у пакета будет иное), можно приступать нашему примеру.

Пример

Будем писать TCP-echo-server. Для работы с TCP-сокетами со стороны сервера используются функции:

  1. uv_tcp_init
  2. uv_tcp_bind
  3. uv_listen
  4. uv_accept

Инициализируем сокет:

uv_tcp_t server;
uv_tcp_init(uv_default_loop(), &server);

 

Подготавливаем его и биндим:

struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", SERVER_PORT, &addr);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0)

 

После чего устанавливаем обработчик входящих соединений и максимальное количество одновременных соединений:

int ret = uv_listen((uv_stream_t*) &server, CONNECTIONS_COUNT, accept_connection_cb);
if(ret) {
    fprintf(stderr, "Listen error %s\n", uv_strerror(ret));
    return 1;
}

 

И запускаем обработчик:

return uv_run(uv_default_loop(), UV_RUN_DEFAULT);

 

Функция обработки входящих соединений выглядит так:

void accept_connection_cb(uv_stream_t *server, int status)
{
    if (status < 0) {
        fprintf(stderr, "New connection error %s\n", uv_strerror(status));
        return;
    }
 
    uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
    uv_tcp_init(uv_default_loop(), client);
 
    if (uv_accept(server, (uv_stream_t*) client) == 0) {
        uv_read_start((uv_stream_t *) client, alloc_buffer_cb, socket_read_cb);
    } else {
        uv_close((uv_handle_t*) client, NULL);
    }
}

 

Да этот пример не тянет на best practice в программирование, хотя бы потому что здесь не проверяются возвращаемые значения, а так же выполнена не вся работа по освобождению данных. Более развернутый пример(как и этот можно посмотреть здесь): https://github.com/Driim/libuv_example

Более развернутый пример — это пример сервера сохраняющего информацию в файлы на диске, подобный тому который я делал на libevent2: https://github.com/Driim/libevent_example

 

При изучении я пользовался официальной документаций и книгой посвященной libuv:

dreamway89

dreamway89 wrote 29 posts

Post navigation


Добавить комментарий

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>