使用file_get_contents指定出口IP和端口

如果我们的机器有多个IP,可能是多个网卡,也可能是一个网卡绑定多个IP,那么我们使用file_get_contents获取网页内容的时候就可以指定使用某个IP和端口作为出口,让对方网站获取我们的IP时候可以做到我们预想的结果。

首先来准备一个显示IP和端口的页面:

getip.php
<?php
echo $_SERVER['REMOTE_ADDR'].':'.$_SERVER['REMOTE_PORT'];

假定我们现在有2个IP,一个127.0.0.1,一个192.168.1.2

<?php
$opts1 = array(
    'socket' => array(
        'bindto' =>'127.0.0.1:0'
    )
);
$opts2 = array(
    'socket' => array(
        'bindto' =>'192.168.1.2:0'
    )
);
$opts3 = array(
    'socket' => array(
        'bindto' =>'0:8888'
    )
);
$opts4 = array(
    'socket' => array(
        'bindto' =>'127.0.0.1:8888'
    )
);

$url = 'http://192.168.1.2/getip.php';

$context1 = stream_context_create($opts1);
$context2 = stream_context_create($opts2);
$context3 = stream_context_create($opts3);
$context4 = stream_context_create($opts4);

echo file_get_contents($url, false, $context1)."\r\n";
echo file_get_contents($url, false, $context2)."\r\n";
echo file_get_contents($url, false, $context3)."\r\n";
echo file_get_contents($url, false, $context4)."\r\n";

上面代码运行结果为:

127.0.0.1:54873
192.168.1.2:54874
192.168.1.2:8888
127.0.0.1:8888

curl, file_get_contents和fsockopen速度比较

测试环境:

  • Apache 2.2
  • PHP 5.3

测试代码:

function curl_test() {
    $url = 'http://www.google.com.hk/';

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    $data = curl_exec($ch);
    curl_close($ch);
    return $data;
}

function file_get_content_test() {
    $url = 'http://www.google.com.hk/';
    $data = file_get_contents($url);
    return $data;
}

function fsockopen_test() {
    $url = 'http://www.google.com.hk/';
    $urls = parse_url($url);
    if(!isset($urls['port'])) {
        $urls['port'] = '80';
    }
    if(!isset($urls['path'])) {
        $urls['path'] = '/';
    }
    $path = $urls['path'];
    if(isset($urls['query'])) {
        $path .= '?'.$urls['query'];
    }

    $data = '';
    $fp = fsockopen($urls['host'], $urls['port'], $errno, $errstr, 10);
    if(!$fp) {
        echo "ERROR: $errno - $errstr <br />\n";
    }
    else {
        $put = "GET ".$path." HTTP/1.1\r\n";
        $put .= "Host: ".$urls['host']."\r\n";
        $put .= "Connection: Close\r\n\r\n";
        fwrite($fp, $put);
        while (!feof($fp)) {
            $data .= fgets($fp);
        }
        fclose($fp);
        if($data) {
            $data = substr($data, strpos($data, "\r\n\r\n")+4);
        }
    }

    return $data;
}

require 'Benchmark/Iterate.php';

$benchmark = new Benchmark_Iterate();

$funcs = array('curl_test', 'file_get_content_test', 'fsockopen_test');
foreach ($funcs as $func) {
    $benchmark->run(10, $func);
    $result = $benchmark->get();
    echo $func;
    var_dump($result);
}

测试结果:

curl_test
array (size=12)
  1 => string '0.439183' (length=8)
  2 => string '0.423215' (length=8)
  3 => string '0.416775' (length=8)
  4 => string '0.416882' (length=8)
  5 => string '0.414696' (length=8)
  6 => string '0.416986' (length=8)
  7 => string '0.418402' (length=8)
  8 => string '0.416467' (length=8)
  9 => string '0.415329' (length=8)
  10 => string '0.417352' (length=8)
  'mean' => string '0.419528' (length=8)
  'iterations' => int 10

file_get_content_test
array (size=12)
  1 => string '0.468215' (length=8)
  2 => string '0.469789' (length=8)
  3 => string '0.473641' (length=8)
  4 => string '0.471970' (length=8)
  5 => string '0.470277' (length=8)
  6 => string '0.472823' (length=8)
  7 => string '0.470583' (length=8)
  8 => string '0.470775' (length=8)
  9 => string '0.470803' (length=8)
  10 => string '0.470506' (length=8)
  'mean' => string '0.470938' (length=8)
  'iterations' => int 10

fsockopen_test
array (size=12)
  1 => string '0.384669' (length=8)
  2 => string '0.386607' (length=8)
  3 => string '0.388593' (length=8)
  4 => string '0.395739' (length=8)
  5 => string '0.390402' (length=8)
  6 => string '0.388805' (length=8)
  7 => string '0.388581' (length=8)
  8 => string '0.390463' (length=8)
  9 => string '0.388577' (length=8)
  10 => string '0.387762' (length=8)
  'mean' => string '0.389019' (length=8)
  'iterations' => int 10