Анализ переносимого потока данных SOAP с помощью PHP

У меня есть корпоративное приложение, которое предоставляет довольно надежный API для сбора данных. В настоящее время я спрашиваю каждого пользователя, что я хочу обновлять в цикле каждую секунду. Однако новая документация API теперь обеспечивает прямой поток всех изменений для всех пользователей. Мне интересно, как я могу анализировать данные в реальном времени, как это происходит с php. Вот несколько деталей:

Данные запрашиваются с помощью SOAP-запроса, и я использую PHP для выполнения таких запросов (пример инициализации сеанса с уникальным идентификатором):

//Get a session ID for this user for the shoretel WEBAPI
$soap_url = 'http://11.11.11.11:8070/ShoreTelWebSDK?wsdl';
$client = new SOAPClient($soap_url, array( 'proxy_host' => '11.11.11.11', 'proxy_port' => 8070, 'trace' => 1 ) );
$client = new SoapClient($soap_url);

$header = new SoapHeader('http://www.ShoreTel.com/ProServices/SDK/Web');
$client->__setSoapHeaders($header);
$registered_string = $client->RegisterClient(array(
                'clientName' => '11.11.11.211'
            )); 
$registered_string = get_object_vars($registered_string);   
$session =  $registered_string['RegisterClientResult'];

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

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

Ответ 1

Хорошо, цель заключается в "потоке ответа SOAP" с "тайм-аутом" и/или в "интервале"?

Я предлагаю переопределить метод SoapClient __doRequest() и реализовать настраиваемое соединение через fsockopen() и передать данные с помощью stream_get_contents(). Теперь вы получаете поток данных XML, а то, что вы хотите, находится посреди него. Вам нужно будет извлечь конверт XML или его части, возможно, используя строковые функции или используя preg_match для извлечения внутреннего содержимого.

Следующий код предоставляет класс SoapClientTimeout, где таймаут устанавливается через stream_set_timeout(). Это используется в случае использования, когда сервер имеет медленные ответы, и вы хотите убедиться, когда прекратить прослушивание.

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

$timeout = 60; // seconds
stream_set_blocking($socket, true);
stream_set_timeout($socket, $timeout); 

Когда у вас есть поток, который блокируется через 1 минуту и ​​закрывается, вам понадобится цикл (с твердым условием выхода), запускающий следующий запрос.


class SoapClientTimeout extends SoapClient
{    
    public function __construct ($wsdl, $options = null)
    {
        if (!$options) $options = [];

        $this->_connectionTimeout = @$options['connection_timeout'] ?: ini_get ('default_socket_timeout');
        $this->_socketTimeout = @$options['socket_timeout'] ?: ini_get ('default_socket_timeout');
        unset ($options['socket_timeout']);

        parent::__construct($wsdl, $options);
    }

    /**
     * Override parent __doRequest and add "timeout" functionality.
     */
    public function __doRequest ($request, $location, $action, $version, $one_way = 0)
    {
        // fetch host, port, and scheme from url.
        $url_parts = parse_url($location);

        $host = $url_parts['host'];
        $port =  @$url_parts['port'] ?: ($url_parts['scheme'] == 'https' ? 443 : 80);
        $length = strlen ($request);

        // create HTTP SOAP request.
        $http_req = "POST $location HTTP/1.0\r\n";
        $http_req .= "Host: $host\r\n";
        $http_req .= "SoapAction: $action\r\n";
        $http_req .= "Content-Type: text/xml; charset=utf-8\r\n";
        $http_req .= "Content-Length: $length\r\n";
        $http_req .= "\r\n";
        $http_req .= $request;

        // switch to SSL, when requested
        if ($url_parts['scheme'] == 'https') $host = 'ssl://'.$host;

        // connect
        $socket = @fsockopen($host, $port, $errno, $errstr, $this->_connectionTimeout);

        if (!$socket) {
            throw new SoapFault('Client',"Failed to connect to SOAP server ($location): $errstr");
        }

        // send request with socket timeout
        stream_set_timeout($socket, $this->_socketTimeout);
        fwrite ($socket, $http_req);

        // start reading the response.
        $http_response = stream_get_contents($socket);

        // close the socket and throw an exception if we timed out.
        $info = stream_get_meta_data($socket);
        fclose ($socket);
        if ($info['timed_out']) {
            throw new SoapFault ('Client', "HTTP timeout contacting $location");
        }

        // the stream contains XML data
        // lets extract the XML from the HTTP response and return it.
        $response = preg_replace (
            '/
                \A       # Start of string
                .*?      # Match any number of characters (as few as possible)
                ^        # Start of line
                \r       # Carriage Return
                $        # End of line
             /smx',
            '', $http_response
        );
        return $response;
    }

}

Ответ 2

Привязать сокет

Функция bind может использоваться для привязки сокета к определенному адресу и порту. Ему нужна структура sockaddr_in, аналогичная функции подключения.

Быстрый пример

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Couldn't create socket: [$errorcode] $errormsg \n");
}

echo "Socket created \n";

// Bind the source address
if( !socket_bind($sock, "127.0.0.1" , 5000) )
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not bind socket : [$errorcode] $errormsg \n");
}

echo "Socket bind OK \n";

Теперь, когда связь выполнена, время, необходимое для подключения сокета к соединениям. Мы связываем сокет с определенным IP-адресом и определенным номером порта. Выполняя это, мы гарантируем, что все входящие данные, направленные на этот номер порта, будут получены этим приложением.

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

Прослушать подключения

После привязки сокета к порту следующая вещь, которую нам нужно сделать, это прослушивание соединений. Для этого нам нужно поставить сокет в режим прослушивания. Функция socket_listen используется для включения сокета в режим прослушивания. Просто добавьте следующую строку после привязки.

//listen
socket_listen ($sock , 10)

Второй параметр функции socket_listen называется backlog. Он контролирует количество входящих соединений, которые сохраняются "в ожидании", если программа уже занята. Таким образом, указывая 10, это означает, что если 10 соединений уже ждут обработки, то 11-й запрос на соединение должен быть отклонен. Это будет более понятным после проверки socket_accept.

Теперь идет основная часть принятия новых соединений.

Принять соединение

Для этого используется функция socket_accept.

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Couldn't create socket: [$errorcode] $errormsg \n");
}

echo "Socket created \n";

// Bind the source address
if( !socket_bind($sock, "127.0.0.1" , 5000) )
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not bind socket : [$errorcode] $errormsg \n");
}

echo "Socket bind OK \n";

if(!socket_listen ($sock , 10))
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("Could not listen on socket : [$errorcode] $errormsg \n");
}

echo "Socket listen OK \n";

echo "Waiting for incoming connections... \n";

//Accept incoming connection - This is a blocking call
$client = socket_accept($sock);

//display information about the client who is connected
if(socket_getpeername($client , $address , $port))
{
    echo "Client $address : $port is now connected to us.";
}

socket_close($client);
socket_close($sock);

Выход

Запустите программу. Он должен показать $ php/var/www/server.php Сокет создан Socket bind OK Socket слушать OK Ожидание входящих соединений...

Итак, теперь эта программа ждет входящих соединений на порту 5000. Не закрывайте эту программу, продолжайте ее работу. Теперь клиент может подключиться к нему на этом порту. Для проверки этого мы будем использовать клиент telnet. Откройте терминал и введите $ telnet localhost 5000

Он сразу же покажет $ telnet localhost 5000 Попытка 127.0.0.1... Подключен к локальному хосту. Символом Escape является "^]". Соединение закрыто внешним хостом.

И вывод сервера покажет Клиент 127.0.0.1: 36689 теперь подключен к нам.

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

Примечание

Функция socket_getpeername используется для получения сведений о клиенте, который подключен к серверу через определенный сокет.

Ответ 3

Я немного озадачен: SOAP (будучи простым протоколом доступа к объектам) полностью противоположна потоковой трансляции. Каждое сообщение SOAP полностью инкапсулировано: оно имеет начало и конец. Это просто невозможно передать.

Вы упомянули о каком-то "новом методе", но не указали никаких подробностей. Обновления "Live" могут быть достигнуты с помощью повторяющихся запросов или может быть фактическим потоком данных. Если он прежний: вы можете уйти с SOAP, но вызов SOAP каждую секунду - это не очень хорошая идея: администратор удаленного сервера не будет доволен таким натиском. Если последнее: это не может быть SOAP. Период.

Если вы не уточните, что делает "новый метод" и как он вызывается, никто не сможет сказать вам, как решить вашу проблему. Но будьте уверены, PHP в наши дни способен на многое. Таким образом, независимо от того, с чем вы сталкиваетесь, вам, конечно, не нужно менять какой-либо другой язык программирования, если вам удобно с PHP. Я предлагаю вам исправить свой первоначальный вопрос и добавить соответствующие данные. Я уверен, что вы найдете здесь много людей, которые могли бы дать вам точное решение.

Ответ 4

Несколько вещей с вашим кодом:

  • Вы дважды создаете экземпляр $client, поэтому первый $client фактически перезаписывается.
  • В заголовке SOAP есть только аргумент пространства имен. Это может не потребоваться.

В вашем коде отображается только идентификатор сеанса. Не очевидно, где данные, может быть, в другом элементе массива $registered_string? Я подозреваю, что после этого вам нужно написать больше кода.

Обычно, по моему опыту, вы должны использовать возвращаемый идентификатор сеанса для создания URL-адреса. Затем вы будете использовать этот URL для доступа к потоку данных. Это будет NOT, а SOAP-вызов, но обычный веб-запрос, используя любой способ, который вы предпочитаете (например, file_get_contents('http://example.com/blah?session=[SESSIONID]')). Сервер будет использовать идентификатор сеанса для проверки подлинности перед возвратом данных.

Если это так, то ответ да, вы можете получить доступ к данным и вставить их в базу данных, но только в том случае, если идентификатор сеанса действителен. Когда идентификатор сеанса истекает, вам снова придется вызвать RegisterClient(). Это будет верно независимо от того, какой язык вы используете.

Ответ 5

Простым способом может быть использование websockets или даже aync-запрос ajax с тайм-аутом в ваш файл soap php.