2010-11-08 29 views
0

對於那個標題感到抱歉,但很難用幾句話來解釋。自定義Web代理:最好是分叉進程或發出http請求?

我寫了一個小的web代理 - 不是Apache或任何一種常見的webserver-誰的作用是執行一些PHP代碼。

有做這兩種方式:

1)派生新的php -f file.php

2)從Web代理中調用http://localhost/file.php

我認爲會有很多的併發請求到代理的,並且每個請求將保持存活至少20-30秒。

我的問題是:這是分叉,並通過HTTP請求之間的更好?

感謝您的任何提示!

Dario

+0

增加了LOOOOOONG示例代碼。 – 2010-11-08 18:20:37

回答

0

最近我也做了一個代理。而且 - 還有第三種選擇。難道甚至不需要自己或其他腳本調用,它是完全獨立的,跨平臺...

所以 - 第一件事是,我已經做到了這一點使用套接字。我想你也是,但只是寫在這裏,以防你沒有。我將瀏覽器中的代理設置爲特定的端口,通過防火牆將其允許進入偵聽的PHP腳本。爲了讓它開始聆聽,我必須在80端口上「運行」腳本,這樣我也可以得到一個很好的實時控制檯。

所以 - 腳本監聽套接字上,並將其設置爲非阻塞。它與循環一起工作(無限,需要時使用break) - * @ socket_accept()*用於查看是否有新連接,如果!== false則檢查。

最後,循環環路(:d)在所有的產生了兒童插座,其被存儲在數組中。它是一個HTTP代理,所以它們都有幾個特定的​​階段(客戶端發送頭文件,嘗試訪問遠程服務器,發送客戶端頭文件,從遠程服務器接收數據)。像讀取這樣的東西(在遠程套接字上打開,使用fsockopen())通常會被阻塞,並且無法將它設置爲非阻塞模式,在非常極端的超時模式下「模擬」非阻塞模式 - 如5微秒,最大。字符讀數是128.

tl; dr;基本上這就像處理器的多線程。

無論其!如果這樣做就需要套接字。

編輯:將所有的自定義操作剝離的示例代碼:(是的,這是很長)

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); 
+0

這不是我正在想的,但你建議我在apache和fork上有一個很好的選擇:編寫另一個web服務器,該角色將執行該file.php。謝謝! – n0cturnal 2010-11-08 20:40:24

+0

有一個非常困難的問題:如果遠程主機連接的代理持續一段時間,這會阻止整個服務器..需要有一個真正的多線程服務器。任何建議? – n0cturnal 2010-11-09 09:42:12

+0

這是一個多線程服務器 - 在PHP中,不與進程。它並沒有真正阻止它 - 正如您所看到的,每次遠程主機的讀取操作都有10微秒的超時時間。這非常快。另外,即使網絡連接非常快,閱讀128個字符以使整個事情變得更快。有些部分可以做得更好,例如寫入遠程服務器 - 目前它不是「線程化」的。 – 2010-11-09 16:13:25