Как получить доступ к набору данных пользовательского контекста в epoll при вызове epoll_wait

#c

Вопрос:

Ниже я добавляю сокеты в epoll и устанавливаю внутри индекс контекста приложения epoll_event.data.u32 .

При получении пакетов recv() требуется дескриптор файла сокета. Во всех примерах events[i].data.fd используется.

Однако events[i].data.fd и events[i].data.u32 находятся в союзе, так как же мне также получить доступ к индексу пользовательского контекста events[i].data.u32 ? Похоже, что он перезаписан дескриптором файла сокета?

 // Initially
int epollFd = epoll_create1(0);

// Adding each socket, along with a user-context index for callbacks
struct epoll_event event;
event.events = EPOLLIN;
event.data.u32 = callbackIndex; // Here is the user-defined index
int sock = createSocket(port, address);
assert(epoll_ctl(epollFd, EPOLL_CTL_ADD, sock, amp;event));

// Later when receiving packets

struct epoll_event events[MAX_EVENTS];

while (true)
{
    int event_count = epoll_wait(epollFd, events, MAX_EVENTS, 30000);
            
    for (i = 0; i < event_count; i  )
    {
        int n = recv(events[i].data.fd, amp;buffer[0], sizeof(buffer), flags);
        // How do I access the user-context index I set when adding the socket to epoll?
    }
}
 

Ответ №1:

Вы указываете epoll_ctl() , для какого дескриптора сокета вы хотите прослушивать события, и предоставляете epoll_event структуру для связи с этой операцией прослушивания.

Всякий epoll_wait() раз, когда вы обнаруживаете зарегистрированное событие в сокете, оно возвращает вам только epoll_event структуру, которую вы предоставили для этого события, точно так же, как вы ее предоставили. Он не сообщает вам, какой сокет вызвал событие.

Итак, если вы хотите обнаружить сокет, вам нужно либо:

  • сохраните сам дескриптор сокета в epoll_event , но тогда вы не сможете использовать какие-либо другие пользовательские данные.
  • сохраните дескриптор сокета где-нибудь в другом месте (т. Е. в массиве, пуле объектов и т. Д.), А Затем поместите идентифицирующую информацию, необходимую для возврата к дескриптору сокета, в виде пользовательских данных epoll_event (т. Е. индекс массива, указатель объекта и т. Д.).

Что бы вы ни вложили в epoll_event «когда звоните epoll_ctl() «, это то, от чего вы получите ответ epoll_wait() . Ни больше, ни меньше.

Ответ №2:

Дизайн epoll удивительно прост. Роль epoll_data_t состоит в том, чтобы обеспечить легкое сопоставление, а не хранение. Обратите внимание, что в нем есть void* ptr элемент, который позволяет вам сопоставлять fd (передавать epoll_ctl ) с чем угодно.

В вашем конкретном случае вы могли бы выделить a struct Context { int fd; uint32_t index; /*...*/ }; в куче и указать на эту структуру EPOLL_CTL_ADD . Вам также придется освободить его после вызова EPOLL_CTL_DEL каким-либо объектом (например, контейнером), которому принадлежит этот контекст.

Поскольку вы используете C , вы можете сохранить указатель на абстрактный EventListener базовый класс, reinterpret_cast начиная с void* epoll_wait этого класса, и отправить событие произвольному производному обработчику.