Соединение fsockopen не закрывается до истечения времени ожидания - PullRequest
5 голосов
/ 04 июля 2011

Фон : Мне нужно создать простой сайт, который принимает входящий размещенный XML и отправляет XML на сервер через сокет-соединение, а затем отображает XML, отправленный обратно с сервера. Легкий горох.

Задача : У меня не было проблем с использованием fsockopen () для подключения к серверу и отправки XML. Чтение XML с сервера было совершенно новой проблемой. Нормальный

while (!feof($fp)) {    
    echo fgets($fp);
}

не сработал, поскольку сервер возвращает одну строку XML и только одну строку XML (без информации о длине, eof, eol и т. Д.). Таким образом, он будет ждать, пока истечет время ожидания, отобразит полученный XML и ошибку времени ожидания. Моя проблема похожа на этого динозавра .

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

Решение : После целого дня борьбы я решил поделиться следующим решением этой проблемы (критикуйте).

$fp = fsockopen("123.456.789.1", 80, $errno, $errstr, 30);
if (!$fp) {
    echo "$errstr ($errno)";
} else {
    $wait = true;    
    $out = '<samplexml><item>something</item></samplexml>';

    // [1] disable blocking
    stream_set_blocking($fp, 0);
    fwrite($fp, $out);

    while (!feof($fp)) {
        $r = fgets($fp);
        echo $r;
        if (!strcmp($r, "")){                
            if (!$wait) {
                // [2] has recieved data on this socket before
                break;
            }
        } else {
            $wait = false;
        }
    }
    fclose($fp);
}

Оказывается, моей главной проблемой была блокировка . Итак, во-первых, [1] мне пришлось отключить stream_set_blocking, чтобы fgets () мог непрерывно проверять, стали ли доступны новые данные. Если не отключено, fgets () получит XML с сервера, а затем цикл будет зависать со второй попытки, поскольку он будет ждать, пока не станет доступно больше данных (чего никогда не будет).

Я знаю, что как только мы прочитаем некоторые данные, мы сможем немедленно закрыть соединение, если будут возвращены пустые fgets () (таким образом, мы все еще можем установить второй параметр fgets ( ) , если это необходимо).

После нескольких месяцев использования этого сайта я наконец-то получил возможность что-то опубликовать в stackoverflow.

Ответы [ 3 ]

2 голосов
/ 04 июля 2011

У фрагмента есть несколько проблем:

  • Во-первых, сравнение strcmp($r, "") на самом деле не имеет смысла.Это работает, но это не имеет смысла.Я не думаю, что fgets когда-либо возвращает пустую строку.Если доступных данных больше нет, возвращается FALSE.Причина вашего сравнения в том, что FALSE преобразуется в пустую строку.Но это делает ваш код неясным.
  • Во-вторых, поскольку у вас есть поток в неблокирующем режиме, данные могут быть недоступны только временно.Выходя из цикла, как только fgets возвращает false, вы потенциально читаете только первый пакет данных, который был кэширован ОС.Вы пытаетесь обойти это, заставляя ждать, пока данные не были получены, но это хрупкое решение.
  • Наконец, вы заняты циклом ожидания данных.Это очень ресурсоемкий процесс.

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

0 голосов
/ 03 апреля 2014

Я использую сокет.Я отправляю строку xml и тоже получаю строку xml.Когда сервер отправляет мне строку '</response>', я должен закрыть сокет.Ниже вы можете найти мой код, если это может кому-то помочь:

            stream_set_blocking($fp, 0);
            fwrite($fp, $in);
            $result = '';

            while (!feof($fp)) {
                $r = fgets($fp);
                $result .= $r;
                //echo $r;
                if (strstr($r, "</response>") != null)
                    break;
            }
            fclose($fp);
0 голосов
/ 04 июля 2011

Вы можете использовать fread() и извлекать по одному байту за раз.Очень неэффективно, но вы могли бы затем проанализировать ответ по мере его поступления и определить, когда заканчивается XML, не дожидаясь истечения времени ожидания соединения.В хакерском псевдокоде:

while(true) {
    $char = fread($fp, 1);
    $xml .= $char;
    if (is_complete($xml)) {
         break;
    }
}
...