自定义Web代理:更好的fork进程或发出http请求?

时间:2010-11-08 17:10:26

标签: php http proxy fork

对不起这个标题,但很难用几句话来解释。

我写了一个小的web代理 - 不是apache或任何类型的常见网络服务器 - 谁的角色是执行一些PHP代码。

有两种方法可以做到:

1)fork一个新的php -f file.php

2)从网络代理中调用http://localhost/file.php

我认为会有很多并发请求到该代理,每个请求将保持活动至少20-30秒。

我的问题是:在分叉和通过http请求之间哪个更好?

感谢任何提示!

的Dario

1 个答案:

答案 0 :(得分:0)

我最近也做了代理。并且 - 有第三种选择。甚至不需要调用自己或其他脚本,它是完全独立的,跨平台的......

所以 - 首先是我使用套接字完成了这个。我猜你也是这样做的,但是如果你不这样做的话,就把它写在这里。我将浏览器中的代理设置为特定端口,允许通过防火墙进入侦听PHP脚本。为了让它开始收听,我必须在端口80上“运行”脚本,所以我也得到了一个很好的实时控制台。

所以 - 脚本侦听套接字,并将其设置为NON-BLOCKING。它适用于循环(无限,需要时使用break断开) - * @ socket_accept()*用于查看是否有任何新连接,检查是否!== false。

最后,循环遍历(:D)所有生成的子套接字,它们存储在一个数组中。它是一个HTTP代理,因此所有这些都有一些特定的阶段(客户端发送标头,尝试访问远程服务器,发送客户端标头,从远程服务器接收数据)。通常会阻止(在使用 fsockopen()打开的远程套接字上读取的内容,并且没有办法将其设置为非阻塞这些,正在“模拟”非阻塞模式非常极端的时间 - 比如5微秒,最大chars read是128。

TL;博士;基本上这就像处理器多线程一样。

无论其 !!!你需要套接字,如果这样做的话。

编辑:添加从所有自定义操作中剥离的示例代码:(是的,它很长)

set_time_limit(0); // so we don't get a timeout from PHP
// - can lead to sockets left open...

$config_port = 85; // port
$config_address = "192.168.0.199"; // IP address, most likely local with router
// if there's not a router between server and wan, use the wan IP
$config_to = 30; // global timeout in seconds
$config_connect_to = 2; // timeout for connecting to remote server
$config_client_to = 0.1; // timeout for reading client data
$config_remote_to = 10; // timeout for reading remote data (microseconds)
$config_remote_stage_to = 15; // timeout for last stage (seconds)
$config_backlog = 5000; // max backlogs, the more the better (usually)

$parent_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // parent socket
$tmp_res = @socket_bind($parent_sock, $config_address, $config_port);
if ($tmp_res === false){
    echo "Can't bind socket.";
    exit;
    // address or port in use, for example by Apache
}

$tmp_res = @socket_listen($parent_sock, $config_backlog);
if ($tmp_res === false){
    echo "Can't start socket listener.";
    exit;
    // hard to tell what can cause this
}

socket_set_nonblock($parent_sock); // non-blocking mode

$sockets = array(); // children sockets
$la = time(); // last activity
while (time() - $la < $config_to){
    $spawn = @socket_accept($parent_sock); // check for new connection
    if ($spawn !== false){
        $la = time();

        $ni = count($sockets);
        $sockets[$ni] = array();
        $sockets[$ni]["handle"] = $spawn;
        $sockets[$ni]["stage"] = 1;
        $sockets[$ni]["la"] = time(); // for some stages
        $sockets[$ni]["client_data"] = "";
        $sockets[$ni]["headers"] = array();
        $sockets[$ni]["remote"] = false;
    }

    foreach ($sockets as &$sock){ // &$sock because we're gonna edit the var
        switch ($sock["stage"]){
        case 1: // receive client data
        $read_data = @socket_read($sock["handle"], 2048);
        if ($read_data !== false && $read_data !== ""){
            $la = time();
            $sock["la"] = microtime(true);
            $sock["client_data"] .= $read_data;
        } else if (microtime(true) - $sock["la"] > $config_client_to) {
            // client data received (or too slow :D)
            $sock["stage"] = 2; // go on
        }
        break;
        case 2: // connect to remote
        $headers = explode("\r\n", $sock["client_data"]);
        foreach ($headers as $hdr){
            $h_pos = strpos($hdr, ":");
            if ($h_pos !== false){
                $nhid = count($sock["headers"]);
                $sock["headers"][strtolower(substr($hdr, 0, $h_pos))] = ltrim(substr($hdr, $h_pos + 1));
            }
        }

        // we'll have to use the "Host" header to know target server
        $sock["remote"] = @fsockopen($sock["headers"]["host"], 80, $sock["errno"], $sock["errstr"], $config_connect_to);
        if ($sock["remote"] === false){
            // error, echo it and close client socket, set stage to 0
            echo "Socket error: #".$sock["errno"].": ".$sock["errstr"]."<br />\n";
            flush(); // immediately show the error
            @socket_close($sock["handle"]);
            $sock["handle"] = 0;
        } else {
            $la = time();
            // okay - connected
            $sock["stage"] = 3;
        }
        break;
        case 3: // send client data
        $tmp_res = @fwrite($sock["remote"], $sock["client_data"]);
        // this currently supports just client data up to 8192 bytes long
        if ($tmp_res === false){
            // error
            echo "Couldn't send client data to remote server!<br />\n";
            flush();
            @socket_close($sock["handle"]);
            @fclose($sock["remote"]);
            $sock["stage"] = 0;
        } else {
            // client data sent
            $la = time();
            stream_set_timeout($sock["remote"], $config_remote_to);
            $sock["la"] = time(); // we'll need this in stage 4
            $sock["stage"] = 4;
        }
        break;
        case 4:
        $remote_read = @fread($sock["remote"], 128);
        if ($remote_read !== false && $remote_read !== ""){
            $la = time();
            $sock["la"] = time();
            @socket_write($sock["handle"], $remote_read);
        } else {
            if (time() - $sock["la"] >= $config_remote_stage_to){
                echo "Timeout.<br />\n";
                flush();
                @socket_close($sock["handle"]);
                @fclose($sock["remote"]);
                $sock["stage"] = 0;
            }
        }
        break;
        }
    }
}

foreach($sockets as $sock){
    @socket_close($sock["handle"]);
    @fclose($sock["remote"]);
}
@socket_close($parent_sock);