咱們知道,從父進程到子常常的數據傳遞相對比較容易一些,可是從子進程傳遞到父進程就比較的困難。php
有不少辦法實現進程交互,在php中比較方便的是 管道通訊。固然,還能夠經過 socket_pair 進行通訊。web
首先是服務器爲了應對每個請求要作的事情(發送一個url 序列,url序列用\t 分割。而結束標記是 \n)服務器
function clientHandle($msgsock, $obj)多線程
{app
$nbuf = '';curl
socket_set_block($msgsock);socket
do {ide
if (false === ($buf = @socket_read($msgsock, 2048, PHP_NORMAL_READ))) {函數
$obj->error("socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)));性能
break;
}
$nbuf .= $buf;
if (substr($nbuf, -1) != "\n") {
continue;
}
$nbuf = trim($nbuf);
if ($nbuf == 'quit') {
break;
}
if ($nbuf == 'shutdown') {
break;
}
$url = explode("\t", $nbuf);
$nbuf = '';
$talkback = serialize(read_ntitle($url));
socket_write($msgsock, $talkback, strlen($talkback));
debug("write to the client\n");
break;
} while (true);
}
上面代碼比較關鍵的一個部分是 read_ntitle,這個函數實現多線程的讀取標題。
代碼以下:(爲每個url fork 一個線程,而後打開管道 ,讀取到的標題寫入到管道里面去,主線程一直的在讀取管道數據,直到全部的數據讀取完畢,最後刪除管道)
function read_ntitle($arr)
{
$pipe = new Pipe("multi-read");
foreach ($arr as $k => $item)
{
$pids[$k] = pcntl_fork();
if(!$pids[$k])
{
$pipe->open_write();
$pid = posix_getpid();
$content = base64_encode(read_title($item));
$pipe->write("$k,$content\n");
$pipe->close_write();
debug("$k: write success!\n");
exit;
}
}
debug("read begin!\n");
$data = $pipe->read_all();
debug("read end!\n");
$pipe->rm_pipe();
return parse_data($data);
}
parse_data 代碼以下,很是的簡單,就不說了。
function parse_data($data)
{
$data = explode("\n", $data);
$new = array();
foreach ($data as $value)
{
$value = explode(",", $value);
if (count($value) == 2) {
$value[1] = base64_decode($value[1]);
$new[intval($value[0])] = $value[1];
}
}
ksort($new, SORT_NUMERIC);
return $new;
}
上面代碼中,還有一個函數read_title 比較有技巧。爲了兼容性,我沒有采用curl,而是直接採用socket 通訊。
在下載到 title 標籤後,就中止讀取內容,以節省時間。代碼以下:
function read_title($url)
{
$url_info = parse_url($url);
if (!isset($url_info['host']) || !isset($url_info['scheme'])) {
return false;
}
$host = $url_info['host'];
$port = isset($url_info['port']) ? $url_info['port'] : null;
$path = isset($url_info['path']) ? $url_info['path'] : "/";
if(isset($url_info['query'])) $path .= "?".$url_info['query'];
if(empty($port)){
$port = 80;
}
if ($url_info['scheme'] == 'https'){
$port = 443;
}
if ($url_info['scheme'] == 'http') {
$port = 80;
}
$out = "GET $path HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.7)\r\n";
$out .= "Connection: Close\r\n\r\n";
$fp = fsockopen($host, $port, $errno, $errstr, 5);
if ($fp == NULL) {
error("get title from $url, error. $errno: $errstr \n");
return false;
}
fwrite($fp, $out);
$content = '';
while (!feof($fp)) {
$content .= fgets($fp, 1024);
if (preg_match("/<title>(.*?)<\/title>/is", $content, $matches)) {
fclose($fp);
return encode_to_utf8($matches[1]);
}
}
fclose($fp);
return false;
}
function encode_to_utf8($string)
{
return mb_convert_encoding($string, "UTF-8", mb_detect_encoding($string, "UTF-8, GB2312, ISO-8859-1", true));
}
這裏,我只是檢測了 三種最多見的編碼。
其餘的代碼都很簡單,這些代碼都是測試用的,若是你要作這樣一個服務器,必定要進行優化處理。特別是,要防止一次打開太多的進程,你要作更多的處理。
不少時候,咱們抱怨php 不支持多進程,實際上,php是支持多進程的。固然,沒有那麼多的進程通訊的選項,而多進程的核心就在於進程的通訊與同步。
在web開發中,這樣的多線程基本上是不會使用的,由於有很嚴重的性能問題。要實現比較簡單的多進程,高負載,必須藉助其擴展。