Ложный eof из feof() с сокетами fgets

Я унаследовал фрагмент кода, который использует функцию fetchURL() ниже для получения данных с URL-адреса. Я только что заметил, что часто feof() возвращает значение true до того, как будет получена полная страница данных. Я пробовал несколько тестов и, используя CURL из file_get_contents(), каждый раз извлекал полную страницу.

Ошибка прерывистая. При 9 вызовах иногда 7 завершаются успешно, а иногда только 4. Определенные 4 из 9 (это запросы на получение только с изменяющейся строкой запроса) всегда завершаются успешно. Я пытался изменить порядок запросы и те же 4 строки запроса по-прежнему всегда успешны, в то время как остальные иногда работают, а иногда нет.
Таким образом, «кажется», что возвращаемые данные могут иметь какое-то отношение к проблеме, но это прерывистый характер это меня обмануло. Данные, возвращаемые в каждом случае, всегда одни и те же (например, каждый раз, когда я делаю вызов со строкой запроса ?SearchString=8502806, возвращаемая страница содержит одни и те же данные), но иногда полная страница доставляется fgets/feof, а иногда нет.

У кого-нибудь есть предложения относительно того, что может быть причиной этой ситуации? Большинство других сообщений, которые O видел на эту тему, касаются противоположной проблемы, из-за которой feof() не возвращает true.

function fetchURL( $url, $ret = 'body' ) {
    $url_parsed = parse_url($url);
    $host = $url_parsed["host"];
    $port = (isset($url_parsed["port"]))?$url_parsed["port"]:'';
    if ($port==0)
        $port = 80;
    $path = $url_parsed["path"];
    if ($url_parsed["query"] != "")
        $path .= "?".$url_parsed["query"];

    $out = "GET $path HTTP/1.0\r\nHost: $host\r\n\r\n";

    $fp = fsockopen($host, $port, $errno, $errstr, 30);

    fwrite($fp, $out);
    $body = false;
    $h = '';
    $b = '';
    while (!feof($fp)) {
        $s = fgets($fp, 1024);
        if ( $body )
            $b .= $s;
        else
            $h .= $s;
        if ( $s == "\r\n" )
            $body = true;
    }

    fclose($fp);

    return ($ret == 'body')?$b:(($ret == 'head')?$h:array($h, $b));
}

person Captain Payalytic    schedule 29.03.2013    source источник
comment
feof на сокетах обычно (всегда?) плохая идея, так как он будет ждать, пока сервер фактически закроет сокет, прежде чем продолжить. По крайней мере, вы также должны отправлять заголовок Connection: close, но я бы серьезно рекомендовал полностью переписать этот код, потому что он просто плохой (без оскорблений).   -  person Tom van der Woerdt    schedule 29.03.2013
comment
Я планирую перейти на CURL, но я хотел знать, что может вызвать проблему, которую я видел. Отсюда вопрос.   -  person Captain Payalytic    schedule 30.03.2013


Ответы (2)


Я вижу довольно много неправильных вещей в этом коде.

  • Никогда не используйте feof в сокетах. Он будет висеть до тех пор, пока сервер не закроет сокет, что не обязательно произойдет сразу после получения страницы.
  • feof может вернуть true (сокет закрыт), пока в буфере PHP все еще есть данные.
  • Ваш код, чтобы отличить заголовок от тела, похоже, полагается на то, что PHP правильно выполняет свою работу, что, как правило, является плохой идеей. fgets не обязательно читает строку, он также может возвращать только один байт (\r, тогда при следующем вызове вы можете получить \n)
  • Вы неправильно кодируете значение пути

Почему бы вам просто не преобразовать свой код для использования cURL или file_get_contents?

person Tom van der Woerdt    schedule 29.03.2013
comment
Я планирую перейти на CURL, но я хотел знать, что может вызвать проблему, которую я видел. Отсюда вопрос. - person Captain Payalytic; 30.03.2013

Это звучит как проблема тайм-аута для меня. См. stream_set_timeout() в руководстве по PHP.

person grahamj42    schedule 29.03.2013
comment
Тайм-ауты похожи на исключения, вы должны использовать их для обнаружения ошибок, если что-то пойдет не так, но не для обычного использования. - person Tom van der Woerdt; 29.03.2013
comment
Тайм-ауты @TomvanderWoerdt неизбежны при получении данных из Интернета. Это разумное объяснение непредсказуемого поведения, не так ли? - person grahamj42; 29.03.2013
comment
Да, тайм-ауты неизбежны, но, судя по вопросу, он ни на какие тайм-ауты не бьет. Определенно не с 20% всех запросов. - person Tom van der Woerdt; 29.03.2013
comment
@TomvanderWoerdt Я согласен, что это плохой код, но поскольку изменяемые данные представляют собой строку запроса, вполне разумно, что удаленный хост выполняет одни запросы дольше, чем другие. Я больше ничего не скажу. - person grahamj42; 30.03.2013