читайте и запишите в тот же сокет (TCP) с помощью выбора

Мы пишем клиенту и серверу, чтобы сделать (что я думал, был), довольно простая сетевая связь. Клиенты Mulitple соединяются с сервером, который тогда, как предполагается, передает данные обратно всем другим клиентам.

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

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

Однако я нашел это, если я использую:

 rv = select(fdmax + 1, &master_list, NULL, NULL, NULL);

Мой код заблокируется, пока не будут новые данные для чтения. Но иногда (асинхронно, от другого потока) у меня будут новые данные для записи на потоке сетевой связи. Так, я хочу, чтобы мой выбор периодически просыпался и позволял мне проверить, существуют ли данные для записи, как:

if (select(....) != -1)
{
  if (FD_SET(sockfd, &master_list))
     // handle data or disconnect
  else
     // look for data to write and write() / send() those.
}

Я пытался установить выбор для опроса режима (или смехотворно короткие тайм-ауты) с:

// master list contains the sockfd from the getaddrinfo/socket/connect seq
struct timeval t;
memset(&t, 0, sizeof t);
rv = select(fdmax + 1, &master_list, NULL, NULL, &t);

но нашли, что тогда тогда клиент никогда не получает входящих данных.

Я также пытался установить сокет fd, чтобы не заблокироваться, как:

fcntl(sockfd, F_SETFL, O_NONBLOCK);

но это не решает проблему:

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

Какие-либо указатели вообще относительно того, что я мог делать неправильно? Разве не возможно сделать чтение-запись на том же сокете (я не могу полагать что быть верным).

(РЕДАКТИРОВАНИЕ: корректный ответ и вещь, которую я помнил на сервере, но не на клиенте, должны иметь второй fd_set и скопировать master_list перед каждым вызовом для выбора ():

// declare and FD_ZERO read_fds:
// put sockfd in master_list

while (1)
{
   read_fds = master_list;
   select(...);

   if (FD_ISSET(read_fds))
     ....
   else
     // sleep or otherwise don't hog cpu resources
}

)

11
задан MarcWan 14 January 2010 в 10:53
поделиться

4 ответа

-

Все выглядит хорошо, кроме линии, где вы делаете , если (fd_set (sockfd, & master_list)) . У меня очень похожая структура кода и использовала FD_isset . Вы должны проверить, установлен ли список, чтобы не установить его снова. Кроме того, я ничего не вижу.

Редактировать. Кроме того, у меня есть следующее для тайм-аута:

timeval listening_timeout;
listening_timeout.tv_sec = timeout_in_seconds;
listening_timeout.tv_usec = 0;

Возможно, есть проблема, если вы установите его на 0 (как вы, кажется, делаете?)

edit2. Я вспомнил, что я столкнулся с странной проблемой, когда я не очищал набор читателей после выбора, и прежде чем я снова ввел его. Я должен был сделать что-то вроде:

FD_ZERO(&sockfd);
FD_SET(sockfd, &rd);

, прежде чем я входил , выберите . Я не могу вспомнить, почему, хотя.

12
ответ дан 3 December 2019 в 05:58
поделиться

Я написал модуль PowerShell под названием VisioPS , который может вам помочь (См. раздел загрузки здесь: * Модуль VisioPS является частью моей библиотеки VisioAutomation на CodePlex )

После установки VisioPS вы запускаете экземпляр PowerShell и делаете следующее:

Import-Module VisioPS
New-VisioApplication
New-VisioDocument
$dg = Import-VisioDirectedGraph c:\foo.xml
Invoke-VisioDraw $dg

Direct Graph - это простой XML-документ, подобный этому

<directedgraph>
  <page>
    <renderoptions
      usedynamicconnectors="true"
      scalingfactor="20"
    />
    <shapes>
      <shape id="n1" label="FOO1" stencil="server_u.vss" master="Server" url="http://microsoft.com" />
      <shape id="n2" label="FOO2" stencil="server_u.vss" master="Email Server" url="http://contoso.com"/>
      <shape id="n3" label="FOO3" stencil="server_u.vss" master="Proxy Server" url="\\isotope\public" />
      <shape id="n4" label="FOO4" stencil="server_u.vss" master="Web Server">
        <customprop name="prop1" value="value1"/>
        <customprop name="prop2" value="value2"/>

      </shape>
      <shape id="n5" label="FOO4" stencil="server_u.vss" master="Application Server" />
    </shapes>

    <connectors>
      <connector id="c1"  from="n1" to="n2" label="LABEL1" />
      <connector id="c2" from="n2" to="n3" label="LABEL2" color="#ff0000" weight="2" />
      <connector id="c3" from="n3" to="n4" label="LABEL1" color="#44ff00" />
      <connector id="c4" from="n4" to="n5" label="" color="#0000ff" weight="5"/>
      <connector id="c5" from="n4" to="n1" label="" />
      <connector id="c6" from="n4" to="n3" label="" weight="10"/>
    </connectors>

  </page>

</directedgraph>
  • VisioPS использует MSAGL для выполнения макета узлов
  • Вы можете легко взять таблицу зависимостей и создать необходимый XML
  • Если посмотреть поближе на код в моей библиотеке, можно также непосредственно создавать объекты, необходимые для выполнения рендеринга, без необходимости вообще проходить через XML.
-121--2823098-

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

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

-121--2948983-

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

1
ответ дан 3 December 2019 в 05:58
поделиться

Я, кажется, вспомним трюк о создании и совместном использовании Filedescriptor чтения / записи между сетевым потоком и основным потоком, который добавляется к дескрипторам в выборе. Эта FD имеет один байт, написанный ему главным потоком, когда оно есть что отправлять. Запись Wack UP Network Thread из вызова SELECT и сетевая резьба снимает данные из общего буфера и записывает его в сеть, а затем вернитесь, чтобы спать в выборе.

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

7
ответ дан 3 December 2019 в 05:58
поделиться

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

0
ответ дан 3 December 2019 в 05:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: