2015-09-04 63 views
12

我有一個企業應用程序,它爲收集數據提供了一個相當強大的API。目前,我正在每秒查詢一個循環中的每個用戶。然而,新的API文檔現在提供了所有用戶的所有更改的實時流。我想知道如何解析這個實時數據,因爲它帶有php。這裏還有一些細節:使用PHP解析實時SOAP數據流

的數據通過一個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完成的事情,還是我需要用另一種語言來實現這一點?

回答

2

好吧,目標是「流式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

綁定的套接字

功能結合可以用來將套接字綁定到特定的地址和端口。它需要一個類似於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 插槽創建 套接字綁定OK 插槽聽OK 等待傳入連接...

所以,現在這個計劃正在等待在端口5000的入站連接。不要關閉這個程序,讓它繼續運行。 現在客戶端可以在此端口上連接到它。我們將使用telnet客戶端來測試它。打開一個終端輸入 $遠程登錄本地主機5000

它會立即顯示 $遠程登錄本地主機5000 試圖127.0.0.1 ... 連到本地主機。 轉義字符是'^]'。 由外國主機關閉的連接。

服務器輸出將顯示 客戶端127.0.0.1:36689現在已連接到我們。

所以我們可以看到客戶端連接到服務器。嘗試上面的步驟,直到你完美的工作。

注意

的socket_getpeername功能是用來獲取它通過特定的套接字連接到服務器的客戶端的詳細信息。

+0

這將如何幫我把SOAP調用的直播內容是什麼? –

+0

您可以按照PHP參考進行更好的說明。 http://php.net/manual/en/function.http-get-request-body.php –

+0

@NavnishBhardwaj你發佈的代碼與「超時」和/或在「流式傳輸SOAP響應」 「間隔」?我現在所看到的並不能解決問題,只是不能解決問題。請解釋爲什麼你的代碼對這個問題有用。 –

0

我有點困惑:SOAP(簡單對象訪問協議)與直播流完全相反。每個SOAP消息都被完全封裝:它有開始和結束。它根本無法流式傳輸。

你已經提到了一些「新方法」,但是你沒有提供任何細節。 「實時」更新可以通過重複請求來實現,或者它可以是實際的數據流。如果它是前者的話:你可以脫身使用SOAP,但每秒調用SOAP並不是一個好主意:遠程服務器管理員不會對這樣的衝擊感到滿意。如果是後者:它不能是SOAP。期。

除非您澄清「新方法」的作用以及如何調用,否則無法告訴您如何解決您的問題。但現在放心PHP幾乎可以做任何事情。因此,無論您遇到什麼問題,如果您對PHP感到滿意,您肯定不需要更改爲某種其他編程語言。我建議你修改你原來的問題並添加相關的細節。我相信你會在這裏找到很多能夠給你確切解決方案的人。

0

有兩件事情與您的代碼:

  1. 您實例$客戶端的兩倍,因此第一$客戶端是有效覆蓋。
  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甚至是超時的aync ajax請求到您的php soap文件。