PHP异步并发connect

以前写过一个multi_http()的函数,就是异步地做http请求,后来再次看那段代码的时候,发现不少问题:
1. 我只在发送、接受数据的时候使用了异步,connect的时候还是同步的
2. 由于我发送数据的时候使用的是http1.0,所以接受数据的时候靠feof()判断结束就很方便了;如果使用http1.1,而且connection:keep-alive; 那么就不是那么简单了

如果需要异步并发,建议使用curl,今天看了一下,curl在异步并发请求的时候,connect、send、recv都是异步的。测试代码:

muti_curl.php
  1.     
    <?php

        

  2.     
    // 创建一对cURL资源

        

  3.     
    $ch1 = curl_init();

        

  4.     
    $ch2 = curl_init();

        

  5.     
     

        

  6.     
    // 设置URL和相应的选项

        

  7.     
    curl_setopt($ch1, CURLOPT_URL, "http://phpor.net/tools/whoami.php");

        

  8.     
    curl_setopt($ch1, CURLOPT_HEADER, 0);

        

  9.     
    curl_setopt($ch2, CURLOPT_URL, "http://phpor.net/tools/whoami.php");

        

  10.     
    curl_setopt($ch2, CURLOPT_HEADER, 0);

        

  11.     
     

        

  12.     
    // 创建批处理cURL句柄

        

  13.     
    $mh = curl_multi_init();

        

  14.     
     

        

  15.     
    // 增加2个句柄

        

  16.     
    curl_multi_add_handle($mh,$ch1);

        

  17.     
    curl_multi_add_handle($mh,$ch2);

        

  18.     
     

        

  19.     
    $active = null;

        

  20.     
    // 执行批处理句柄

        

  21.     
    do {

        

  22.     
            $mrc = curl_multi_exec($mh, $active);

        

  23.     
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

        

  24.     
     

        

  25.     
    while ($active && $mrc == CURLM_OK) {

        

  26.     
            if (curl_multi_select($mh) != 1) {

        

  27.     
                    do {

        

  28.     
                            $mrc = curl_multi_exec($mh, $active);

        

  29.     
                    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

        

  30.     
            }

        

  31.     
    }

        

  32.     
     

        

  33.     
    // 关闭全部句柄

        

  34.     
    curl_multi_remove_handle($mh, $ch1);

        

  35.     
    curl_multi_remove_handle($mh, $ch2);

        

  36.     
    curl_multi_close($mh);

        

使用strace观察一下:
strace -tt php multi_curl.php

connect(3,{sa_family=AF_INET,sin_port=htons(80), sin_addr=inet_addr("66.147.244.189")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT}], 1, 0)    = 0

虽然是异步,这里还是立即检查了一下是否已经连接成功;但是这种检查也是非阻塞的(看poll的第三个参数)。如果connect连接的是本地端口,poll检查的时候连接就已经是成功的了。 如(10.55.38.14是本机):
connect(4, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.55.38.14")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=4, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1

——————————————
下面这篇文章写的并不好,仅作学习之用:

这里关键是socket_select的用法了;
另:
1. 当阻塞方式connect的时候,设置连接超时时间是通过设置SO_SNDTIMEO来实现的,如:
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array(‘sec’ => 3, ‘usec’ => 0));
设置超时时间为3s;
2. 对于非阻塞方式的connect,连接的超时时间不是在socket上设置的,而是自己控制循环的时间

multi_connect.php
  1.     
    <?php

        

  2.     

        

  3.     
    $arrTarget = array(

        

  4.     
        array("host"=>"10.55.38.61", "port"=>80),

        

  5.     
        array("host"=>"66.147.244.18", "port"=>80),

        

  6.     
        array("host"=>"10.55.38.63", "port"=>82),

        

  7.     
    );

        

  8.     
    $arrResult = multi_connect($arrTarget);

        

  9.     

        

  10.     
    print_r($arrResult);

        

  11.     
    exit;

        

  12.     

        

  13.     
    function multi_connect($arrTarget) {

        

  14.     
        $arrSocket = array();

        

  15.     
        foreach($arrTarget as $key=>$pair) {

        

  16.     
            $socket  = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        

  17.     
            socket_set_nonblock($socket);

        

  18.     
            @socket_connect($socket, $pair["host"], $pair["port"]);

        

  19.     
            $arrSocket[$key] = $socket;

        

  20.     
        }

        

  21.     
        $arrLeft = $arrSocket;

        

  22.     
        $arrSocketWrite = $arrSocket;

        

  23.     
        $arrSocketRead = null;

        

  24.     
        $arrExcept = array();

        

  25.     
        $all = count($arrTarget);

        

  26.     
        $arrOk = array();

        

  27.     

        

  28.     
        $timeout = 5;

        

  29.     
        $oritimeout = $timeout;

        

  30.     

        

  31.     
        while($all > 0 && $timeout > 0) {

        

  32.     
            $time_start = time();

        

  33.     
            $done = socket_select($arrSocketRead, $arrSocketWrite,$arrExcept,$timeout);

        

  34.     
            $timeout -= (time() $time_start);

        

  35.     
            if ($done <= 0) {

        

  36.     
                //error or timeout

        

  37.     
                echo "Timeout $oritimeout(s)n";

        

  38.     
                break;

        

  39.     
            }

        

  40.     
            $all -= $done;

        

  41.     

        

  42.     
            foreach($arrSocketWrite as $key=>$val) {

        

  43.     
                $arrOk[$key] = $val;

        

  44.     
            }

        

  45.     
            $arrLeft = array_diff($arrLeft, $arrSocketWrite);

        

  46.     

        

  47.     
            echo "n—-connect ok:——————–n";

        

  48.     
            print_r($arrSocketWrite);

        

  49.     
            echo "n—-except:——————–n";

        

  50.     
            print_r($arrExcept);

        

  51.     
            echo "n–left:———————-n";

        

  52.     
            print_r($arrSocketWrite);

        

  53.     

        

  54.     
            echo "n====================================n";

        

  55.     

        

  56.     
            $arrSocketWrite = $arrLeft;

        

  57.     

        

  58.     
        }

        

  59.     
        return $arrOk;

        

  60.     
    }

        

留下评论

邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据