2012-02-26 1 views
2

У меня есть сервер чата в C/Linux с использованием сокетов TCP. При использовании libev я могу создать ev_io watcher для чтения событий один раз для сокета. Что-то вроде:Libev - обратные вызовы ввода-вывода

ev_io* new_watcher = (ev_io*)malloc(sizeof(ev_io)); 

//initialize the watcher 
ev_init(new_watcher, read_cb); 

//set the fd and event to fire on write 
ev_io_set(new_watcher, watcher->fd, EV_READ); 

//start watching 
ev_io_start(loop, new_watcher); 

, и это работает отлично, потому что событие чтения будет срабатывать только при наличии данных для чтения. Тем не менее, я должен обрабатывать записи событий по-разному, потому что они постоянно стреляют, даже когда у меня нет данных для записи. Чтобы решить эту проблему, у меня есть read_callback, создающий ev_io watcher для записи данных только тогда, когда есть данные, готовые к записи, а затем write_callback удалит наблюдателя после его отправки.

Это означает, что я выделяю, инициализацию, настройку, просмотр, разблокировку и освобождение наблюдателя записи каждый раз, когда мне нужно обрабатывать сообщение. Я беспокоюсь, что, возможно, это неправильно и неэффективно.

Каков наилучший метод обработки событий write_callback в libev?

Заранее спасибо.

ответ

0

Выделение может добавить некоторые накладные расходы, вы можете использовать статическую переменную вместо malloc или malloc один раз и только после завершения цикла события. Вам нужно установить только перед записью и отменить после того, как она преуспеет. Но да, вот как это нужно сделать.

0

Способ, которым я решил эту ситуацию, состоял в том, чтобы иметь функцию для записи данных, которая берет указатель на буфер и длину. Он хранит указатель и длину в структуре данных очереди и включает событие записи.

Когда срабатывает обратный вызов события записи, он проверяет очередь записи, чтобы увидеть, есть ли ожидающие записи. Если они есть, она берет следующую ожидающую запись в очереди и записывает ее в дескриптор файла. Затем, перед тем как выйдет обратный вызов записи, он проверяет, пуста ли ожидающая очередь записи. Если это так, то это отключает событие записи.

Если вы сделаете свои глобальные переменные объектов чтения/записи, они будут только распределены и освобождены один раз. Вы включаете событие записи всякий раз, когда знаете, что есть данные, которые нужно записать, и вы отключите его, как только больше не будет записано данных.

Мой код немного сложнее описанного выше описания, но я разместил ссылку здесь, чтобы вы могли посмотреть. Код, о котором я говорю конкретно, находится в файле aiofd.h и aiofd.c (aiofd == Асинхронный дескриптор файла ввода-вывода): https://bitbucket.org/wookie/cutil/

Надеюсь, это поможет.

4

Простой, существует также ev_io_stop, поэтому вы не запускаете наблюдателя записи, если у вас нет записи, и внутри обратного вызова вы вызываете ev_io_stop, когда вы написали весь буфер.

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

В соответствии с вышеприведенными предположениями вы почти никогда не должны запускать наблюдателя записи. Недостатком является гораздо более сложный код, поэтому во многих случаях лучше всего начинать с простого «добавления данных для записи буфера, запуска наблюдателя, внутри наблюдателя остановить его, если буфер был полностью написан».