2012-12-12 58 views
6

我一直在並行運行兩個套接字客戶端,收集HTTP流數據(不是Twitter,但類似的東西)。數據通過分塊編碼完成。丟失6個字節,但只有套接字安靜60秒?

其中一個客戶端是curl(在命令行上,而不是php-curl),其中http和https都可以正常工作。另一個是我自己的PHP腳本,使用fsockopenfgets。對https工作正常,但我有一個特定的問題與http。具體如何?只有當流靜音60秒纔會發生。如果只有50秒的安靜,它工作正常。我一直在比較發送和接收到的curl的http頭文件,並刪除了所有的差異。我以爲我知道所有關於PHP套接字的知識,尤其是分塊編碼,但是現在是時候吃下這個蹩腳的餡餅了,因爲這個餡餅已經讓我難住了。

因此,運行具有「--trace - --trace時間」嫋嫋,我看到這個來通過與第一包60秒的平靜期後:

05:56:57.025023 <= Recv data, 136 bytes (0x88) 
0000: 38 32 0d 0a 7b 22 64 61 74 61 66 65 65 64 22 3a 82..{"datafeed": 
0010: 22 64 65 6d 6f 2e 31 64 36 2e 31 6d 2e 72 61 6e "demo.1d6.1m.ran 
... 
0080: 34 22 7d 5d 7d 0a 0d 0a       4"}]}... 

的82尺寸爲十六進制的大塊。 \ r \ n標記塊大小行的結尾。該塊從「{」開始。

在在PHP端,我的循環開始像這樣:

while(true){ 
    if(feof($fp)){fclose($fp);return "Remote server has closed\n";} 
    $chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length 
    $len=hexdec($chunk_info); //$len includes the \r\n at the end of the chunk (despite what wikipedia says) 

使用HTTPS,或者用小於60第二間隙,這工作得很好,$ len爲100或任何塊大小。 但是,有60第二間隙後,我得到$ chunk_info是:

datafeed":"demo.1d6.1m.ran... 

所以,我似乎失去了前六個字節:38 32 0d 0a 7b 22

所有後續塊是罰款,並完全與捲曲接收的相同。


版本詳情

捲曲7.19.7下(x86_64-PC-Linux的GNU)的libcurl/7.19.7的OpenSSL/0.9.8k的zlib/1.2.3.3的libidn/1.15 協議:TFTP FTP遠程登錄字典LDAP LDAPS HTTP文件HTTPS FTPS 特點:GSS-協商IDN的IPv6 Largefile NTLM SSL libz進行

PHP 5.3.2-1ubuntu4.18用了Suhosin貼片(CLI)(建:2012年9月12日19:12: 47)

服務器:Apache/2.2.14(Ubuntu的)

(我只與本地主機連接測試,到目前爲止)


循環的其餘部分是這樣的:

$s=''; 
$len+=2; //For the \r\n at the end of the chunk 
while(!feof($fp)){ 
    $s.=fread($fp,$len-strlen($s)); 
    if(strlen($s)>=$len)break; //TODO: Can never be >$len, only ==$len?? 
    } 
$s=substr($s,0,-2); 
if(!$s)continue; 
$d=json_decode($s); 
//Do something with $d here 
} 

(旁白:在路上我到目前爲止測試的代碼已經通過這個循環通過恰好一次,60秒靜默期之前。)


注意:我有許多解決方法可以使事情順利進行:例如強制使用https,或使用curl-php。這個問題是因爲我想知道發生了什麼,知道60秒後發生了什麼變化,並學習如何阻止它發生。也許學習一個新的故障排除的想法。把它看作是流血的知識分子的好奇心:-)

+0

好奇心:爲什麼不從PHP的cURL開始? –

+0

我們能否看到循環的其餘部分,尤其是使用套接字緩衝區的部分? –

+0

@FrancisAvila感謝您的興趣,只是發佈它。 –

回答

1

在這裏工作的bug修復:

$chunk_info=trim(fgets($fp)); //First line is hex digits giving us the length 
if($chunk_info=='')continue; //Usually means the default 60 second time-out on fgets() was reached. 
... 

如果與fgets($ FP)返回東西那麼你有一大塊閱讀。如果這個東西是零,那麼你有一個空白的塊來處理。但是當它返回沒有什麼這意味着fgets已經超時。它顯示tcp://的默認超時值是60秒;而ssl://的默認超時時間更長(對不起,我還沒有追蹤到它是什麼 - 它可能是它永遠阻止)。

通過在沒有任何可讀的情況下嘗試處理塊時,所有內容都不同步。因此被盜的6個字節。

故障排除提示:

  1. 產仔的代碼:echo "**".date("Y-m-d H:i:s");print_r(stream_get_meta_data($fp));ob_flush();flush();元數據有一個條目說,當最後一個動作流過超時。日期戳是必不可少的。
  2. 從命令行與tcpdump -A -i lo port http交叉引用。比較時間戳與PHP中調試行的時間戳,讓我發現可疑的行爲。
1

說實話,我不完全確定你的代碼爲什麼會這樣,但它讀取數據的方式是不正確的;我將它改寫爲這樣的:

$chunkmeta = fgets($f); 
// chunked transfer can have extensions (indicated by semi-colon) 
// see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html 
$chunklen = hexdec(current(explode(';', $chunkmeta, 2))); 

// chunk length is non-zero 
if ($chunklen) { 
    $chunk = ''; 
    // only read up to chunk length 
    while (!feof($f) && $chunklen) { 
     if ($buf = fread($f, $chunklen)) { 
      $chunklen -= strlen($buf); 
      $chunk .= $buf; 
     } else { 
      // uh oh, something bad happened 
      die("Could not read chunk"); 
     } 
    } 
    if ($chunklen == 0) { 
     // read the trailing CRLF 
     fread($f, 2); // read CRLF 
     // data is ready 
    } 
} 

更新

應與這一個我的直覺去了(雖然上面的代碼應該只是罰款無論哪種方式);默認情況下,default_socket_timeout設置爲60秒,所以後續讀取應該返回false。仍然沒有解釋爲什麼它會在安全套接字;-)

+0

順便說一句,你知道任何使用「;」的網絡服務器,還是這只是未來的證明? AFAIK Apache從來沒有給我發過一個。 –

+0

偉大的代碼,傑克和我(現在!)看到它沒有我有的錯誤。 –

+0

我從來沒有見過使用chunk擴展,但其語法描述在[這裏](http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html) –

相關問題