Управляемая событиями модель в C с сокетами

Я действительно заинтересован в программировании на C на событиях, особенно с использованием сокетов, поэтому я собираюсь посвятить некоторое время своим исследованиям.

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

Я знаю, что это сложно, но всегда приятно знать, как работают разные решения.

Ответы

Ответ 1

Вы обязательно должны прочитать следующее: http://www.kegel.com/c10k.html. Эта страница является идеальным обзором событийных и асинхронных методов.

Однако быстрый и грязный ответ: управляемый событиями не является ни неблокирующим, ни асинхронным.

Event-driven означает, что процесс будет отслеживать свои файловые дескрипторы (и сокеты) и действовать только тогда, когда какое-то событие происходит на каком-то дескрипторе (события: полученные данные, ошибка, становятся доступными для записи,...).

Сокеты BSD имеют функцию "select()". При вызове ОС будет контролировать дескрипторы и вернуться к процессу, как только произойдет какое-либо событие на одном из дескрипторов.

Однако на веб-сайте выше есть намного лучшие описания (и сведения о различных API-интерфейсах).

Ответ 2

"Какова философия этой модели"

Событие управляется означает, что нет "мониторинга", но сам событие инициирует действие.

Обычно это инициируется прерыванием, которое является сигналом к ​​системе с внешнего устройства или (в случае программного прерывания) асинхронным процессом.

https://en.wikipedia.org/wiki/Interrupt

Дальнейшее чтение, кажется, здесь:

https://docs.oracle.com/cd/E19455-01/806-1017/6jab5di2m/index.html#sockets-40  - "Входы/выходы с управляемым прерыванием"

Кроме того, http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html содержит некоторые примеры сокетов с прерываниями, а также другие примеры программирования сокетов.

Ответ 3

Программирование на основе событий основано на цикле событий. Цикл просто ждет нового события, отправляет код для обработки события, затем возвращается назад, чтобы дождаться следующего события. В случае сокетов вы говорите об "асинхронном сетевом программировании". Это включает select() или какой-либо другой вариант, например Kqueue() для ожидания событий в цикле событий. Сокеты должны быть установлены как неблокирующие, поэтому при чтении() или записи() ваш код не будет ждать завершения ввода-вывода.

Асинхронное сетевое программирование может быть очень сложным и сложным для правильного. Ознакомьтесь с несколькими введениями здесь и здесь. Я настоятельно рекомендую использовать библиотеку, такую ​​как libevent или liboop, чтобы получить это право.

Ответ 4

Такие TCP-серверы/клиенты могут быть реализованы с помощью select(2) вызовов и неблокирующих сокетов.

Более сложно использовать неблокирующие сокеты, чем блокировать сокеты.

Пример:

connect вызов обычно возвращает -1 немедленно и устанавливает errno EINPROGRESS, когда используется неблокирующий сокет. В этом случае вы должны использовать select для ожидания, когда соединение будет открыто или не будет выполнено. connect также может возвращать 0. Это может произойти, если вы создаете соединение с локальным хостом. Таким образом, вы можете обслуживать другие сокеты, в то время как один сокет открывает TCP-соединение.

Ответ 5

На самом деле это очень специфичная платформа для того, как это работает.

Если вы работаете в Linux-системе, это действительно не сложно, вам просто нужно создать копию своего процесса, используя "fork", что-то вроде следующего: трюк:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet.h>
#include <signal.h>
#include <unistd.h>

int main()
{
  int server_sockfd, client_sockfd;
  int server_len, client_len;
  struct sockaddr_in server_address;
  struct sockaddr_in client_address;

  server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_Address.sin_port = htons(1234);
  server_len = sizeof(server_address);
  bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

  listen(server_sockfd, 5);

  signal(SIGCHLD, SIG_IGN);

  while(1)
  {
    char ch;
    printf("Server Waiting\n");
    client_len = sizeof(client_address);
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len)

    // Here where we do the forking, if you've forked already then this will be the child running, if not then your still the parent task.

    if(fork() == 0)
    {
      // Do what ever the child needs to do with the connected client
      read(client_sockfd, &ch, 1);
      sleep(5); // just for show :-)
      ch++;
      write(client_sockfd, &ch, 1);
      close(client_sockfd);
      exit(0);
    }
    else
    {
      // Parent code here, close and loop for next connection
      close(client_sockfd);
    }
  }
}

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

Использование fork, однако, является стандартным способом сделать это на C в системе на основе Linux/Unix.

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

Вы можете (я считаю, что так или иначе) по-прежнему использовать стандартные сокеты berkley под окнами, но он полны ловушек и дыр, для windows winsock это хорошее место для начала:

http://tangentsoft.net/wskfaq/

Насколько я знаю, если вы используете Winsock, у него есть материал, чтобы помочь с нерестом и несколькими клиентами, я лично лично, однако, я обычно просто откручиваю отдельный поток и копирую сокет-соединение с ним, тогда вернитесь в цикл, слушая мой сервер.