Возможная ошибка - Связывание потока с FD, который уже имеет связанный с ним? - PullRequest
1 голос
/ 06 февраля 2010

Хауди. Я нахожусь в сетевом классе, и мы создаем наш собственный "сетевой API", используя функции сокетов, которые мы изучили в классе.

Для задания профессор предоставил уже готовые программы чата и сервера, и мы должны были заполнить пустой файл .c, который имел связанный заголовочный файл, описывающий вызовы функций в нашем маленьком «сетевом API», что чат и сервер используемые программы.

Я близок к завершению, но столкнулся с проблемой. Я собирался пойти и поиграть с моим кодом, но все остальные части моего «сетевого API» работают, поэтому я не хотел с ним связываться, когда я так близок к завершению и имею небольшое изменение, потому что другие части не работать. Кроме того, я не уверен на 100%, что я действительно знаю, в чем заключается моя ошибка.

Моя проблема в моей функции recv_line, которая должна имитировать recv ().

Моя функция recv_line получает дескриптор файла от клиента для соединения, которое было установлено с сервером. Затем я связываю поток с дескриптором файла с помощью fdopen. Бит кода выглядит так:

  // Associate a stream with the sock_fd
  if (NULL == (fdstream = fdopen(sock_fd, "r"))) {
    return -1;
  }

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

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

Я предполагаю, что это происходит, потому что данные, отправленные клиенту, находятся в исходном потоке (тот, который был создан первый раз), и во второй раз, когда я вызываю fdopen, во втором созданном мною потоке нет данных ? Я не совсем уверен, как работает fdopen, когда я вызываю его во второй раз?

С другой стороны, я читаю мои данные таким образом, поскольку мы должны продолжать читать данные до тех пор, пока мы не прочитаем EOF или последовательность символов \ r \ n. Я использую getc и ungetc с потоком, чтобы обеспечить механизм просмотра вперед, который будет проверять EOF или последовательность \ r \ n.

Итак, при всем этом, кто-нибудь знает, что именно происходит с моим большим кодом и программой? Я гуглил метод, чтобы проверить, есть ли у FD связанный с ним поток, и он не смог ничего найти / увидеть. Я провел некоторое исследование, чтобы увидеть, была ли какая-то функция времени fstat, которая позволяла бы мне видеть связанные пары и ничего не придумала. Могу ли я просто скопировать FD, который передается в функцию recv_line, а затем fdopen на дублированном FD и избежать всех этих проблем?

Спасибо за помощь. Мне жаль, если я не был достаточно ясен. Я сделаю все возможное, чтобы уточнить что-либо, если вы попросите меня сделать это. И извините, мой вопрос был таким длинным. = * (* * Тысяча двадцать-две

Еще раз спасибо. Я очень ценю вашу помощь.

Ответы [ 3 ]

2 голосов
/ 06 февраля 2010

Это из справочной страницы fdopen:

Функция fdopen () связывает поток с существующим файлом дескриптор, фд. Режим потока (одно из значений «r», «r +», «w», «w +», «a», «a +») должны быть совместимы с режимом файла дескриптор. Индикатор положения файла нового потока установлен на принадлежащие fd, а также индикаторы ошибок и конца файла очищено. Режимы "w" или "w +" не вызывают усечение файла. The дескриптор файла не дублируется и будет закрыт при закрытии потока, созданного функцией fdopen (). [emph. мой]

Другими словами, насколько я понимаю (это не моя область знаний), ваша идея «перехватывать» дескриптор файла сокета снова и снова на самом деле является проблемой, потому что когда вы по это первый раз, вы не только закрываете свой поток, но и сокетное соединение вместе с ним.

2 голосов
/ 06 февраля 2010

Я предполагаю, что это происходит, потому что данные, отправленные клиенту, находятся в исходном потоке (тот, который был создан в первый раз), и во второй раз, когда я вызываю fdopen, не осталось никаких данных в дескриптор файла для в втором потоке для чтения Я создал ?

Это в значительной степени правильно (с некоторыми незначительными исправлениями). Основная обязанность объекта stream заключается в считывании потока данных файловых дескрипторов из ядра в пользовательскую область, поэтому вам не нужны накладные расходы на системный вызов. Но как только объект потока читает поток данных, ядро ​​выдвигает его «указатель чтения», поэтому в следующий раз, когда вы попытаетесь прочитать данные из файлового дескриптора, он обнаружит, где вы остановились.

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

Когда вы вызываете fdopen, вы больше не должны ничего делать с файловым дескриптором - даже не закрывайте его (когда вы вызываете fclose, базовый дескриптор будет закрыт).

Исходя из комментария Джонатона Леффлера, я полагаю, что код профессора также закроет дескриптор. Если это правда, вы не можете использовать fdopen.

Некоторые идеи:

  1. Просто прочитайте байты fd. Не очень производительно, но будет работать.
  2. Разрешено ли считать, что ваш код обрабатывает только одно соединение за раз? Если это так, просто используйте статический буфер в recv_line, который буферизует данные fd. Заполните буфер, когда он пуст, и затем прочитайте данные из него.
  3. Если вам нужно обрабатывать несколько соединений, вы можете расширить # 2, чтобы иметь один буфер на дескриптор файла. Для этого вам понадобится какая-то карта.

Обновление на основе комментария

@ Крис - позвольте мне объяснить риск использования fdopen. После того, как вы использовали fdopen для файлового дескриптора, вы должны вызвать fclose (или вы рискуете вытечь ресурсы, выделенные для FILE *), и вы не можете вызвать close для дескриптора (так как fclose для дескриптора вы можете столкнуться с проблемами при закрытии дескриптора несколько раз). Таким образом, если вашему коду уже не делегирована ответственность за закрытие дескриптора (в этом случае вы можете просто вызвать fclose), вы не можете использовать fdopen.

Не существует встроенного способа обнаружения вызова fdopen в дескрипторе. Вы должны добавить логику самостоятельно. Вам нужно будет сохранить коллекцию файлов FILE *, возвращаемых fdopen; если у вас уже был ФАЙЛ * для данного дескриптора, вы бы использовали его, а затем вызывали бы fdopen.

1 голос
/ 06 февраля 2010

Я не уверен, действительно ли я понимаю вашу проблему, но я бы предложил открывать входящие / исходящие потоки непосредственно при создании сокета и сохранять их в структуре вместе с сокетом, пока сокет действителен (связано). Таким образом, вам не нужно открывать новые потоки для каждой строки. Функция recv_line будет иметь входной поток в качестве параметра вместо сокета.

...