最近我也做了一個代理。而且 - 還有第三種選擇。難道甚至不需要自己或其他腳本調用,它是完全獨立的,跨平臺...
所以 - 第一件事是,我已經做到了這一點使用套接字。我想你也是,但只是寫在這裏,以防你沒有。我將瀏覽器中的代理設置爲特定的端口,通過防火牆將其允許進入偵聽的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);
增加了LOOOOOONG示例代碼。 – 2010-11-08 18:20:37