2016-03-29 31 views
1

私は接続の「多少妨げられたインターネットタイプ」としてしか説明できないファイルをダウンロードするのに、curl_multiを使用しています。カールで接続が切断された状態を正しく処理する方法

CURLOPT_TIMEOUT_MSの既定値を使用すると、アプリケーションが無期限に停止します。したがって、ファイルはかなり大きいので(300kbファイルをダウンロードするのに約20分かかります)、私はそれを適切な値の1,800,000(30分)で設定します。問題は、ダウンロードが5分遅れると、ハンドルが解放されるまでに25分待たなければならないということです。

この問題を解決する私の最初のアイデアは、タイムアウトイベントのために30秒の範囲内に小さなタイムアウトを使用し、次にcurl_infoをチェックすることでした。タイムアウトした場合は、範囲ヘッダーを使用してプロセスを再起動します。

ここで重大な欠陥がありますが、サーバーが複数の接続をフラッドとして表示してブロックするか、サーバーが範囲ヘッダーをサポートしていない可能性があります(ダウンロードを最初のバイトから開始する必要があります)。

紛失またはリセット接続を検出する別の方法はありますか?

私はcurl_multi_selectへの呼び出しでタイムアウト値を使用していますので、リリースするにはcurlを待たずにコードを実行できます。

答えて

1

curl_multi_selectをタイムアウトで使用している場合は、特定の期間、またはすべてのハンドルが一定期間データを受信しなかった場合に、接続を閉じることができるはずです。唯一の問題は、あなたが仕事ができる任意のデータ

$timeout  = 45; // abort after 45 seconds with no data 
$lastReceived = 0; // time data was last received 

while ($active && $mrc == CURLM_OK) { 
    if (curl_multi_select($mh) != -1) { 
     do { 
      $mrc = curl_multi_exec($mh, $active); 
      $lastReceived = time(); 
     } while ($mrc == CURLM_CALL_MULTI_PERFORM); 
    } else { 
     if (time() - $lastReceived > $timeout) { 
      // no data received within timeout 
      // close cURL handles & multi connection and restart 
     } 
    } 
} 

を受信して​​いないが、あなたが特定のハンドルをきめ細かく制御を見つける与えない扱うかわからないです。

エラーが発生するずっと前にそれらを検出したい状況でタイムアウトを処理した最善の方法は、とCURLOPT_WRITEFUNCTIONを組み合わせて使用​​しています。

私はコードをマルチインターフェイスに変換しませんが、必要に応じて既存のコードに変換することができます。

考えられるのは、独自のタイムアウトを定義し、ハンドルが最後にデータを受信した日時を記録することです。その時間内にデータが受信されなかった場合は、転送を中止してやり直すことができます。

データを毎秒取得する必要があるため、このタイムラインを早期に検出したいと考えています。すべての種類の接続問題で非常にうまく動作し、テストでは、cURLがエラーアウトするよりもはるかに迅速にタイムアウトが検出されたことがわかりました。 CURLOPT_WRITEFUNCTIONを使用しての

1つの副作用は、あなたがそれは残念ながらそれほど単純ではないのですが、それは動作しますcurl_exec

<?php 

$readTimeout = 45; // number of seconds to time out after if no data received 
$lastReceived = 0; // time data was last received on the handle 
$buffer  = ''; // buffer for storing response (you'll need one for each handle) 

//...when initializing cURL 
curl_setopt($ch, CURLOPT_NOPROGRESS, false); 
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 'progressFunction'); 
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'writeFunction'); 
// don't set anything for CURLOPT_RETURNTRANSFER 

$result = curl_exec($ch); 
$info = curl_getinfo($ch); 
$error = curl_errno($ch); 
// $result will be false on any failure or timeout 
// check $info['http_code'] for HTTP response status 
// $error will be empty if no error occurred 
// on success, $buffer will contain the full response body 
// if failed, you can try reconnecting and resending the request until successful 

//... 

function writeFunction($handle, $data) 
{ 
    global $lastReceived, $buffer; // <-- I use class properties instead 

    $lastReceived = time(); 
    $size = strlen($data); 

    $buffer .= $data; 

    return $size; 
} 

function progressFunction($ch, $dltotal, $dlnow, $ultotal, $ulnow) 
{ 
    global $lastReceived, $readTimeout; 

    $time = time(); 

    if ($time - $lastReceived > $readTimeout) { 
     // set error state - no data received within timeout 
     return 1; // non-zero causes cURL to disconnect 
    } 

    return 0; 
} 

からデータを取得するためにCURLOPT_RETURNTRANSFERを使用し、それが読んだとしてデータを保存する必要はないだろうということですあなたがしたいことをうまくやっています。希望が役立ちます。

+0

私がタイムアウトに持っている最大の問題は、接続が終了することです。時間の範囲内でダウンロードサイズを追跡する提案された方法は、完全に機能します。詳細な回答を書く時間をとってくれてありがとう。 – Twifty

+0

あなたが助けてくれることを歓迎します。私のユースケースはきわめて明確で、progressFunctionとwriteFunctionの両方に他のコードを用意していました。拡張するために、connect/requestロジックは基本的に永遠に実行され、タイムアウト/エラーの後で再接続する無限ループ内に存在します(progressFunctionまたはcURLから強制的に強制されます)。 – drew010

関連する問題