Несколько дочерних процессов чтения / записи по одному каналу - PullRequest
3 голосов
/ 22 августа 2011

В настоящее время я изучаю программирование сокетов с использованием C в среде Linux.В качестве проекта я пытаюсь написать базовый чат-сервер и клиент.

Цель состоит в том, чтобы сервер развернул процесс для каждого подключенного клиента.

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

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

Поскольку этот канал является общим для всех дочерних элементовЗатем каждый ребенок должен прочитать данные на канале.Это не работает, поскольку конвейер данных, оказывается, не может быть прочитан каждым дочерним процессом одновременно и дочерними процессами, которые «пропускают» блок данных в вызове read.

Ниже приведен код в дочернем процессе, который делает это:

for( ; ; )
{
  rset = mset;
  if(select(maxfd+1, &rset, NULL, NULL, NULL) > 0)
  {
    if(FD_ISSET(clientfd, &rset))
    {
      read(clientfd, buf, sizeof(buf));
      write(pipes[1], buf, strlen(buf));
    }
    if(FD_ISSET(pipes[0], &rset))
    {
      read(pipes[0], buf, sizeof(buf));
      write(clientfd, buf, sizeof(buf));
    }
  }
}

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

Спасибо

Ответы [ 2 ]

2 голосов
/ 23 августа 2011

Каждый байт данных, записанных в канал, будет прочитан ровно один раз. Он не дублируется для каждого процесса с открытым концом чтения канала.

Если вы хотите, чтобы данные дублировались для нескольких процессов назначения, вам необходимо явно дублировать данные. Например, у вас может быть один «главный» процесс, который имеет канал к каждому «подчиненному» процессу. Когда ведомое устройство хочет передать сообщение другим ведомым, оно отправляет его ведущему процессу, который зацикливается и записывает его один раз в каждый канал, идущий к другим подчиненным.

2 голосов
/ 22 августа 2011

Чтобы обойти проблему дочернего процесса, читающего из канала больше данных, чем нужно (и, в свою очередь, заставляющего другого дочернего элемента «застрять» при попытке чтения из пустого канала), вам, вероятно, следует использовать любое сообщение POSIX очереди или один канал между родительским и отдельным дочерними процессами, а не один глобальный канал для связи между родительским и дочерним процессами. В настоящее время, когда сервер пишет в канал для связи со своими дочерними элементами, он не может точно определить, какой дочерний элемент будет читать из канала в любой момент времени, так как планирование процессов ОС является недетерминированным. , Другими словами, без какого-либо механизма синхронизации или барьеров чтения / записи, если сервер выполняет запись в канал, в вашем коде нет ничего, что мешало бы одному дочернему элементу «пропустить» чтение, а второму дочернему элементу - двойное чтение. оставив другого ребенка, который должен был получить передаваемые данные с сервера, голодным и поэтому заблокированным.

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

Если вы не хотите обрабатывать несколько каналов для каждого дочернего элемента в родительском процессе сервера, вы можете использовать глобальную очередь сообщений, используя очереди сообщений POSIX (см. mqueue.h). При таком подходе, если ребенок захватывает сообщение, которое он не должен иметь (то есть вам нужно будет передать struct, содержащий некоторый тип значения идентификатора), он поместит сообщение обратно в очередь и попытается прочитайте другое сообщение ... это не так эффективно по скорости, как подход с прямым каналом, но оно позволит вам записать обратно сообщение, которое не предназначено для текущего потомка, без чередующихся осложнений, которые будут иметь место с глобальным труба или механизм FIFO.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...