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

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

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

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


person iNDicator    schedule 16.06.2012    source источник
comment
Если вы собираетесь делать какие-либо вещи с сокетами Linux, я бы порекомендовал руководство Beejs здесь beej.us/guide/bgnet< /а>   -  person mathematician1975    schedule 16.06.2012
comment
Спасибо за рекомендацию, эта книга объясняет или содержит какой-либо справочник по программированию, управляемому событиями?   -  person iNDicator    schedule 16.06.2012
comment
Непонятно, о чем вы спрашиваете, но вы можете начать с чтения, например, документации libevent.   -  person Artefacto    schedule 16.06.2012
comment
@iINDicator - насколько я знаю, нет. Если вы уже довольны сокетами в Linux в целом, вам не нужно больше узнавать. Я предложил его на тот случай, если вы совершенно не знакомы с сокетами, поскольку это руководство действительно помогло мне начать работу.   -  person mathematician1975    schedule 16.06.2012
comment
@Artefacto - меня интересует дизайн программы, в которой она может обрабатывать несколько соединений без многопоточности или порождения процессов, а только один главный процесс (управляемый событиями). Информация, которую я нашел, ограничена, поэтому я хотел бы услышать комментарии от людей с большим опытом в этой области.   -  person iNDicator    schedule 16.06.2012
comment
Это интересный вопрос. Я считаю, что nodsjs и nginx реализованы с системным вызовом select. Системный вызов select может отслеживать множество одновременных подключений, не создавая новых процессов. Эти события выбора затем отправляются в цикл событий nodejs (т.е. ваши обратные вызовы javascript).   -  person Mike76    schedule 10.11.2019


Ответы (5)


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

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

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

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

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

person Frunsi    schedule 16.06.2012
comment
Спасибо за ссылку, кажется, это то, что я ищу ;) - person iNDicator; 16.06.2012
comment
Этот ответ неправильный. управление событиями по своей природе асинхронно. Ничего не мониторит. Само событие порождает операции. Смотрите мой ответ для более подробной информации. - person Graham; 14.08.2016
comment
@Graham: управляемый событиями и асинхронный часто используются как синонимы. Но (по крайней мере, в мире Linux) это разные вещи. Это спорно. Прочтите ответы на этот вопрос: stackoverflow.com/questions/5844955/ - person Frunsi; 15.08.2016

"какая философия стоит за этой моделью"

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

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

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 содержит несколько примеров сокетов, управляемых прерываниями, а также другие примеры программирования сокетов.

person Graham    schedule 12.08.2016

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

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

person Matt    schedule 16.06.2012

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

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

Пример:

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

person SKi    schedule 20.06.2012

На самом деле это очень зависит от платформы, как это работает.

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

#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's 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.

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

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

http://tangentsoft.net/wskfaq/

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

person shawty    schedule 16.06.2012
comment
Большое спасибо за ваш пример, однако разве вилка не создает новый дочерний процесс? Итак, если бы у нас было подключено 30 клиентов, разве мы не получили бы 30 новых процессов? - person iNDicator; 16.06.2012
comment
Действительно, он создает дочерние процессы, но это не полномасштабные процессы, а частичные задачи, отпочковавшиеся от родителя, которые длятся только в течение срока службы службы. Другой способ, которым вы могли бы это сделать, - использовать Linux PThreads, который по-прежнему порождает подпроцесс, но не в своем собственном пуле памяти, как это делает fork. Подавляющее большинство сервисов Linux используют способ ведения дел Fork. - person shawty; 16.06.2012
comment
Ну, это то, что я хочу предотвратить, см. пост Фрунси. это, кажется, то, что мне нужно! не то чтобы твой бесполезен ;) - person iNDicator; 17.06.2012
comment
Не беспокойтесь и не обижайтесь. :-) - person shawty; 17.06.2012