使用PHP解析实时SOAP数据流

12

我有一个企业应用程序,提供了相当强大的API来收集数据。目前,我正在每秒循环查询我想要更新的每个用户。然而,新的API文档现在提供了所有用户的所有更改的实时流。我想知道如何在php中解析这些实时数据。以下是一些细节:

通过SOAP请求请求数据,并且我正在使用PHP进行这些请求,例如(返回具有唯一ID的会话启动的示例):

//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实现的吗?还是需要寻找另一种语言来实现这个功能?
5个回答

3
好的,目标是“流式传输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;
    }

}

0

我有点困惑:SOAP(简单对象访问协议)与实时流完全相反。每个SOAP消息都是完全封装的:它有开始和结束。它根本无法被流式传输。

你提到了一些“新方法”,但你没有给出任何细节。“实时”更新可以通过重复请求或实际数据流来实现。如果是前者:你可以使用SOAP,但每秒钟调用SOAP并不是一个好主意:远程服务器管理员不会对这样的攻击感到满意。如果是后者:它不能是SOAP。就是这样。

除非你澄清“新方法”的作用以及如何调用它,否则没有人能告诉你如何解决你的问题。但请放心,PHP这些天可以做任何事情。所以无论你面临什么问题,如果你熟悉PHP,你肯定不需要改变到其他编程语言。我建议你修改原始问题并添加相关细节。我相信你会在这里找到很多人能够给你确切的解决方案。


0

你的代码有一些问题:

  1. 你两次实例化了$client,所以第一个$client实际上被覆盖了。
  2. 你的SOAP头只有命名空间参数。可能不需要它。

你的代码似乎只注册了一个会话ID,不清楚数据在哪里,也许在$registered_string数组的另一个元素中?我怀疑你需要在这之后编写更多的代码。

通常,在我的经验中,你会使用返回的会话ID来构造URL。然后,你将使用该URL来访问数据流。这不是一个SOAP调用,而是使用你喜欢的任何方法(例如file_get_contents('http://example.com/blah?session=[SESSIONID]'))进行的常规Web请求。服务器将使用会话ID验证你,然后返回数据。

如果是这样工作的话,那么答案是是的,你可以在会话ID有效期内访问数据并将其插入到数据库中,但是会话ID过期后,你将不得不再次调用RegisterClient()。不管你使用什么语言,这都是真的。


0

简单的方法可能是使用WebSockets或带有超时的异步Ajax请求到您的PHP SOAP文件。


-1

绑定套接字

函数bind可用于将套接字绑定到特定的地址和端口。它需要一个类似于connect函数的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 套接字已创建 套接字绑定成功 套接字监听成功 等待传入连接...

现在,此程序正在端口5000上等待传入连接。不要关闭此程序,让它保持运行状态。 现在,客户端可以连接到此端口。我们将使用telnet客户端来测试。打开终端并键入 $ telnet localhost 5000

它会立即显示 $ telnet localhost 5000 正在尝试127.0.0.1... 已连接到localhost。 转义字符为'^]'。 连接由远程主机关闭。

服务器输出将显示 客户端127.0.0.1:36689现在已连接到我们。

因此,我们可以看到客户端已连接到服务器。尝试以上步骤,直到完美运行。

注意

socket_getpeername函数用于获取通过特定套接字连接到服务器的客户端的详细信息。


这将如何帮助我获取SOAP调用的实时内容? - June Lewis
您可以查看PHP参考文档,以更好地理解此内容。http://php.net/manual/en/function.http-get-request-body.php - Navnish Bhardwaj
@NavnishBhardwaj,您发布的代码与“流式传输SOAP响应”以及“超时”和/或“间隔”有何关联?就目前而言,我没有看到解决问题的方法,也不是问题的答案。请解释一下您的代码为什么对该问题有用。 - Jens A. Koch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接