PHP静态类

classShtml
{
var$Templet;
var$DataSource;
var$Dir;
var$fileName;
var$mod;
var$handle;
functionShtml($fileName="")
{
$this->fileName=$fileName;
$this->mod="wb";
$this->handle=false;
$this->Templet = "";
$this->DataSource = array();
$this->Dir = "";
}
/// <描述>
/// 绑定数据源,参数为一数组。
///
functionBindData($arr)
{
$this->DataSource = $arr;
}
/// <描述>
/// 设置文件存放路径。
///
functionSetDir($dir)
{
$this->Dir = $dir;
}
functionSetFileName($fileName)
{
return$this->fileName=$fileName;
}
functionGetMod()
{
return$this->mod;
}
functionSetMod($mod)
{
return$this->mod=$mod;
}
functionOpen()
{
if(substr($this->fileName,0,1)=="/")
$this->fileName = $_SERVER['DOCUMENT_ROOT'] . $this->fileName;
if($this->handle=fopen($this->fileName, $this->mod))
return$this->handle;
else
returnfalse;
}
functionClose()
{
returnfclose($this->handle);
}
functionWrite($content)
{
returnfwrite($this->handle,$content);
}
functionMkDir($pathname)
{
$currentPath="";
str_replace("\","/",$pathname);
$pathArr = split("/",$pathname);
if($pathArr[0] == "") //使用绝对路径
{
$currentPath = $_SERVER['DOCUMENT_ROOT'];
}
else
{
$currentPath = $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']);
}
for($i=0; $i
/// 生成静态文件。
///
function Create()
{
$tmp = $this->Templet;
foreach($this->DataSource as $key=>$value)
{
$tmp = str_replace("", $value, $tmp);
}
$this->MkDir(dirname($this->fileName));
$this->Open();
$this->Write($tmp);
$this->Close();
}
}
function CreateShtml()
{
ob_start("callback_CteateShtml");
}
function callback_CteateShtml($buffer)
{
$page = intval(@$_REQUEST["page"]);
$shtml = new Shtml();
$shtml->SetFileName($_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']) . "/" . basename($_SERVER['PHP_SELF'],".php") . ($page==0 ? "" : "_" . strval($page)) . ".htm");
$shtml->Templet = $buffer;
$shtml->Create();
return $buffer;
}
?>

生成静态页面的PHP类

classhtml
{
var$dir; //dir for the htmls(without/)
var$rootdir; //root of html files(without/):html
var$name; //html文件存放路径
var$dirname; //指定的文件夹名称
var$url; //获取html文件信息的来源网页地址
var$time; //html文件信息填加时的时间
var$dirtype; //目录存放方式:year,month,,,,
var$nametype; //html文件命名方式:name
functionhtml($nametype='name',$dirtype='year',$rootdir='html')
{
$this->setvar($nametype,$dirtype,$rootdir);
}
functionsetvar($nametype='name',$dirtype='year',$rootdir='html')
{
$this->rootdir=$rootdir;
$this->dirtype=$dirtype;
$this->nametype=$nametype;
}
functioncreatedir($dir='')
{
$this->dir=$dir?$dir:$this->dir;
if(!is_dir($this->dir))
{
$temp = explode('/',$this->dir);
$cur_dir = '';
for($i=0;$itime=$time?$time:$this->time;
$this->dirname=$dirname?$dirname:$this->dirname;
switch($this->dirtype)
{
case'name':
if(empty($this->dirname))
$this->dir=$this->rootdir;
else
$this->dir=$this->rootdir.'/'.$this->dirname;
break;
case'year':
$this->dir=$this->rootdir.'/'.date("Y",$this->time);
break;
case'month':
$this->dir=$this->rootdir.'/'.date("Y-m",$this->time);
break;
case'day':
$this->dir=$this->rootdir.'/'.date("Y-m-d",$this->time);
break;
}
$this->createdir();
return$this->dir;
}
functiongeturlname($url='')
{
$this->url=$url?$url:$this->url;
$filename=basename($this->url);
$filename=explode(".",$filename);
return$filename[0];
}
functiongeturlquery($url='')
{
$this->url=$url?$url:$this->url;
$durl=parse_url($this->url);
$durl=explode("&",$durl[query]);
foreach($durlas$surl)
{
$gurl=explode("=",$surl);
$eurl[]=$gurl[1];
}
returnjoin("_",$eurl);
}
functiongetname($url='',$time=0,$dirname='')
{
$this->url=$url?$url:$this->url;
$this->dirname=$dirname?$dirname:$this->dirname;
$this->time=$time?$time:$this->time;
$this->getdir();
switch($this->nametype)
{
case'name':
$filename=$this->geturlname().'.htm';
$this->name=$this->dir.'/'.$filename;
break;
case'time':
$this->name=$this->dir.'/'.$this->time.'.htm';
break;
case'query':
$this->name=$this->dir.'/'.$this->geturlquery().'.htm';
break;
case'namequery':
$this->name=$this->dir.'/'.$this->geturlname().'-'.$this->geturlquery().'.htm';
break;
case'nametime':
$this->name=$this->dir.'/'.$this->geturlname().'-'.$this->time.'.htm';
break;
}
return$this->name;
}
functioncreatehtml($url='',$time=0,$dirname='',$htmlname='')
{
$this->url=$url?$url:$this->url;
$this->dirname=$dirname?$dirname:$this->dirname;
$this->time=$time?$time:$this->time;
//上面保证不重复地把变量赋予该类成员
if(empty($htmlname))
$this->getname();
else
$this->name=$dirname.'/'.$htmlname; //得到name
$content=file($this->url)ordie("Failed to open the url ".$this->url." !");;
///////////////关键步---用file读取$this->url
$content=join("",$content);
$fp=@fopen($this->name,"w")ordie("Failed to open the file ".$this->name." !");
if(@fwrite($fp,$content))
returntrue;
else
returnfalse;
fclose($fp);
}
/////////////////以name为名字生成html
functiondeletehtml($url='',$time=0,$dirname='')
{
$this->url=$url?$url:$this->url;
$this->time=$time?$time:$this->time;
$this->getname();
if(@unlink($this->name))
returntrue;
else
returnfalse;
}
/**
* function::deletedir()
* 删除目录
* @param $file 目录名(不带/)
* @return
*/
functiondeletedir($file)
{
if(file_exists($file))
{
if(is_dir($file))
{
$handle =opendir($file);
while(false!==($filename=readdir($handle)))
{
if($filename!="."&&$filename!="..")
$this->deletedir($file."/".$filename);
}
closedir($handle);
rmdir($file);
returntrue;
}else{
unlink($file);
}
}
}
}
?>

apache mod_limitipconn 的配置方法

在你的apache的conf文件里面加入

LoadModule limitipconn_module lib/apache/mod_limitipconn.so

ExtendedStatus On

下面就是apache mod_limitipconn的配置了

有两种基本配置,第一种是对整个服务器进行配置,第二种是对单个虚拟主机进行配置,区别就是配置参数是否放在VirtualHost里面。

MaxConnPerIP 3 后面的3代表最多允许单个ip同时3个请求

NoIPLimit image/* 代表image目录不进行ip限制

OnlyIPLimit audio/mpeg video 代表只限制音频视频文件


    
	MaxConnPerIP 3
	NoIPLimit image/*
    

    
	MaxConnPerIP 1
	OnlyIPLimit audio/mpeg video
    


上面的第一段代表在用户访问/somewhere目录时,最多允许单ip3个连接,但是image目录下的文件不进行计数。

上面的第二断代表用户在访问/mp3目录时,最多允许单ip1个链接,仅仅对audio/mpeg video类型的文件访问时进行限制

当然了最简单的就是这样了


    
        MaxConnPerIP 2
    

Google文件系统

转载自互连网
Google文件系统

GFS是一个可扩展的分布式文件系统,用于大型的、分布式的、对大量数据进行访问的应用。它运行于廉价的普通硬件上,但可以提供容错功能。它可以给大量的用户提供总体性能较高的服务。
1、设计概览
(1)设计想定
GFS与过去的分布式文件系统有很多相同的目标,但GFS的设计受到了当前及预期的应用方面的工作量及技术环境的驱动,这反映了它与早期的文件系统明显不同的设想。这就需要对传统的选择进行重新检验并进行完全不同的设计观点的探索。
GFS与以往的文件系统的不同的观点如下:
1、部件错误不再被当作异常,而是将其作为常见的情况加以处理。因为文件系统由成百上千个用于存储的机器构成,而这些机器是由廉价的普通部件组成并被大量的客户机访问。部件的数量和质量使得一些机器随时都有可能无法工作并且有一部分还可能无法恢复。所以实时地监控、错误检测、容错、自动恢复对系统来说必不可少。
2、按照传统的标准,文件都非常大。长度达几个GB的文件是很平常的。每个文件通常包含很多应用对象。当经常要处理快速增长的、包含数以万计的对象、长度达TB的数据集时,我们很难管理成千上万的KB规模的文件块,即使底层文件系统提供支持。因此,设计中操作的参数、块的大小必须要重新考虑。对大型的文件的管理一定要能做到高效,对小型的文件也必须支持,但不必优化。
3、大部分文件的更新是通过添加新数据完成的,而不是改变已存在的数据。在一个文件中随机的操作在实践中几乎不存在。一旦写完,文件就只可读,很多数据都有这些特性。一些数据可能组成一个大仓库以供数据分析程序扫描。有些是运行中的程序连续产生的数据流。有些是档案性质的数据,有些是在某个机器上产生、在另外一个机器上处理的中间数据。由于这些对大型文件的访问方式,添加操作成为性能优化和原子性保证的焦点。而在客户机中缓存数据块则失去了吸引力。
4、工作量主要由两种读操作构成:对大量数据的流方式的读操作和对少量数据的随机方式的读操作。在前一种读操作中,可能要读几百KB,通常达 1MB和更多。来自同一个客户的连续操作通常会读文件的一个连续的区域。随机的读操作通常在一个随机的偏移处读几个KB。性能敏感的应用程序通常将对少量数据的读操作进行分类并进行批处理以使得读操作稳定地向前推进,而不要让它来来回回的读。
5、工作量还包含许多对大量数据进行的、连续的、向文件添加数据的写操作。所写的数据的规模和读相似。一旦写完,文件很少改动。在随机位置对少量数据的写操作也支持,但不必非常高效。
6、系统必须高效地实现定义完好的大量客户同时向同一个文件的添加操作的语义。
(2)系统接口
GFS提供了一个相似地文件系统界面,虽然它没有向POSIX那样实现标准的API。文件在目录中按层次组织起来并由路径名标识。
(3)体系结构:
一个GFS集群由一个master和大量的chunkserver构成,并被许多客户(Client)访问。如图1所示。Master和 chunkserver通常是运行用户层服务进程的Linux机器。只要资源和可靠性允许,chunkserver和client可以运行在同一个机器上。
文件被分成固定大小的块。每个块由一个不变的、全局唯一的64位的chunk-handle标识,chunk-handle是在块创建时由 master分配的。ChunkServer将块当作Linux文件存储在本地磁盘并可以读和写由chunk-handle和位区间指定的数据。出于可靠性考虑,每一个块被复制到多个chunkserver上。默认情况下,保存3个副本,但这可以由用户指定。
Master维护文件系统所以的元数据(metadata),包括名字空间、访问控制信息、从文件到块的映射以及块的当前位置。它也控制系统范围的活动,如块租约(lease)管理,孤儿块的垃圾收集,chunkserver间的块迁移。Master定期通过HeartBeat消息与每一个 chunkserver通信,给chunkserver传递指令并收集它的状态。
与每个应用相联的GFS客户代码实现了文件系统的API并与master和chunkserver通信以代表应用程序读和写数据。客户与master的交换只限于对元数据(metadata)的操作,所有数据方面的通信都直接和chunkserver联系。
客户和chunkserver都不缓存文件数据。因为用户缓存的益处微乎其微,这是由于数据太多或工作集太大而无法缓存。不缓存数据简化了客户程序和整个系统,因为不必考虑缓存的一致性问题。但用户缓存元数据(metadata)。Chunkserver也不必缓存文件,因为块时作为本地文件存储的。
(4)单master。
只有一个master也极大的简化了设计并使得master可以根据全局情况作出先进的块放置和复制决定。但是我们必须要将master对读和写的参与减至最少,这样它才不会成为系统的瓶颈。Client从来不会从master读和写文件数据。Client只是询问master它应该和哪个 chunkserver联系。Client在一段限定的时间内将这些信息缓存,在后续的操作中Client直接和chunkserver交互。
以图1解释一下一个简单的读操作的交互。
1、client使用固定的块大小将应用程序指定的文件名和字节偏移转换成文件的一个块索引(chunk index)。
2、给master发送一个包含文件名和块索引的请求。
3、master回应对应的chunk handle和副本的位置(多个副本)。
4、client以文件名和块索引为键缓存这些信息。(handle和副本的位置)。
5、Client 向其中一个副本发送一个请求,很可能是最近的一个副本。请求指定了chunk handle(chunkserver以chunk handle标识chunk)和块内的一个字节区间。
6、除非缓存的信息不再有效(cache for a limited time)或文件被重新打开,否则以后对同一个块的读操作不再需要client和master间的交互。
通常Client可以在一个请求中询问多个chunk的地址,而master也可以很快回应这些请求。
(5)块规模:
块规模是设计中的一个关键参数。我们选择的是64MB,这比一般的文件系统的块规模要大的多。每个块的副本作为一个普通的Linux文件存储,在需要的时候可以扩展。
块规模较大的好处有:
1、减少client和master之间的交互。因为读写同一个块只是要在开始时向master请求块位置信息。对于读写大型文件这种减少尤为重要。即使对于访问少量数据的随机读操作也可以很方便的为一个规模达几个TB的工作集缓缓存块位置信息。
2、Client在一个给定的块上很可能执行多个操作,和一个chunkserver保持较长时间的TCP连接可以减少网络负载。
3、这减少了master上保存的元数据(metadata)的规模,从而使得可以将metadata放在内存中。这又会带来一些别的好处。
不利的一面:
一个小文件可能只包含一个块,如果很多Client访问改文件的话,存储这些块的chunkserver将成为访问的热点。但在实际应用中,应用程序通常顺序地读包含多个块的文件,所以这不是一个主要问题。
(6)元数据(metadata):
master存储了三中类型的metadata:文件的名字空间和块的名字空间,从文件到块的映射,块的副本的位置。所有的metadata都放在内存中。前两种类型的metadata通过向操作日志登记修改而保持不变,操作日志存储在master的本地磁盘并在几个远程机器上留有副本。使用日志使得我们可以很简单地、可靠地更新master的状态,即使在master崩溃的情况下也不会有不一致的问题。相反,mater在每次启动以及当有 chuankserver加入的时候询问每个chunkserver的所拥有的块的情况。
A、内存数据结构:
因为metadata存储在内存中,所以master的操作很快。进一步,master可以轻易而且高效地定期在后台扫描它的整个状态。这种定期地扫描被用于实现块垃圾收集、chunkserver出现故障时的副本复制、为平衡负载和磁盘空间而进行的块迁移。
这种方法的一个潜在的问题就是块的数量也即整个系统的容量是否受限与master的内存。实际上,这并不是一个严重的问题。Master为每个 64MB的块维护的metadata不足64个字节。除了最后一块,文件所有的块都是满的。类似的,每个文件的名字空间数据也不足64个字节,因为文件名是以一种事先确定的压缩方式存储的.如果要支持更大的文件系统,那么增加一些内存的方法对于我们将元数据(metadata)保存在内存种所获得的简单性、可靠性、高性能和灵活性来说,这只是一个很小的代价。
B、块位置:
master并不为chunkserver所拥有的块的副本的保存一个不变的记录。它在启动时通过简单的查询来获得这些信息。Master可以保持这些信息的更新,因为它控制所有块的放置并通过HeartBeat消息来监控chunkserver的状态。
这样做的好处:因为chunkserver可能加入或离开集群、改变路径名、崩溃、重启等,一个集群重有成百个server,这些事件经常发生,这种方法就排除了master与chunkserver之间的同步问题。
另一个原因是:只有chunkserver才能确定它自己到底有哪些块,由于错误,chunkserver中的一些块可能会很自然的消失,这样在master中就没有必要为此保存一个不变的记录。
C、操作日志:
操作日志包含了对metadata所作的修改的历史记录。它作为逻辑时间线定义了并发操作的执行顺序。文件、块以及它们的版本号都由它们被创建时的逻辑时间而唯一地、永久地被标识。
操作日志是如此的重要,我们必须要将它可靠地保存起来,并且只有在metadata的改变固定下来之后才将变化呈现给用户。所以我们将操作日志复制到数个远程的机器上,并且只有在将相应的日志记录写到本地和远程的磁盘上之后才回答用户的请求。
Master可以用操作日志来恢复它的文件系统的状态。为了将启动时间减至最小,日志就必须要比较小。每当日志的长度增长到超过一定的规模后,master就要检查它的状态,它可以从本地磁盘装入最近的检查点来恢复状态。
创建一个检查点比较费时,master的内部状态是以一种在创建一个检查点时并不耽误即将到来的修改操作的方式来组织的。Master切换到一个新的日子文件并在一个单独的线程中创建检查点。这个新的检查点记录了切换前所有的修改。在一个有数十万文件的集群中用一分钟左右就能完成。创建完后,将它写入本地和远程的磁盘。
(7)数据完整性
名字空间的修改必须是原子性的,它们只能有master处理:名字空间锁保证了操作的原子性和正确性,而master的操作日志在全局范围内定义了这些操作的顺序。
文件区间的状态在修改之后依赖于修改的类型,不论操作成功还是失败,也不论是不是并发操作。如果不论从哪个副本上读,所有的客户都看到同样的数据,那么文件的这个区域就是一致的。如果文件的区域是一致的并且用户可以看到修改操作所写的数据,那么它就是已定义的。如果修改是在没有并发写操作的影响下完成的,那么受影响的区域是已定义的,所有的client都能看到写的内容。成功的并发写操作是未定义但却是一致的。失败的修改将使区间处于不一致的状态。
Write操作在应用程序指定的偏移处写入数据,而record append操作使得数据(记录)即使在有并发修改操作的情况下也至少原子性的被加到GFS指定的偏移处,偏移地址被返回给用户。
在一系列成功的修改操作后,最后的修改操作保证文件区域是已定义的。GFS通过对所有的副本执行同样顺序的修改操作并且使用块版本号检测过时的副本(由于chunkserver退出而导致丢失修改)来做到这一点。
因为用户缓存了会位置信息,所以在更新缓存之前有可能从一个过时的副本中读取数据。但这有缓存的截止时间和文件的重新打开而受到限制。
在修改操作成功后,部件故障仍可以是数据受到破坏。GFS通过master和chunkserver间定期的handshake,借助校验和来检测对数据的破坏。一旦检测到,就从一个有效的副本尽快重新存储。只有在GFS检测前,所有的副本都失效,这个块才会丢失。
2、系统交互
(1)租约(lease)和修改顺序:
(2)数据流
我们的目标是充分利用每个机器的网络带宽,避免网络瓶颈和延迟
为了有效的利用网络,我们将数据流和控制流分离。数据是以流水线的方式在选定的chunkerserver链上线性的传递的。每个机器的整个对外带宽都被用作传递数据。为避免瓶颈,每个机器在收到数据后,将它收到数据尽快传递给离它最近的机器。
(3)原子性的record Append:
GFS提供了一个原子性的添加操作:record append。在传统的写操作中,client指定被写数据的偏移位置,向同一个区间的并发的写操作是不连续的:区间有可能包含来自多个client的数据碎片。在record append中, client只是指定数据。GFS在其选定的偏移出将数据至少原子性的加入文件一次,并将偏移返回给client。
在分布式的应用中,不同机器上的许多client可能会同时向一个文件执行添加操作,添加操作被频繁使用。如果用传统的write操作,可能需要额外的、复杂的、开销较大的同步,例如通过分布式锁管理。在我们的工作量中,这些文件通常以多个生产者单个消费者队列的方式或包含从多个不同 client的综合结果。
Record append和前面讲的write操作的控制流差不多,只是在primary上多了一些逻辑判断。首先,client将数据发送到文件最后一块的所有副本上。然后向primary发送请求。Primary检查添加操作是否会导致该块超过最大的规模(64M)。如果这样,它将该块扩充到最大规模,并告诉其它副本做同样的事,同时通知client该操作需要在下一个块上重新尝试。如果记录满足最大规模的要求,primary就会将数据添加到它的副本上,并告诉其它的副本在在同样的偏移处写数据,最后primary向client报告写操作成功。如果在任何一个副本上record append操作失败,client将重新尝试该操作。这时候,同一个块的副本可能包含不同的数据,因为有的可能复制了全部的数据,有的可能只复制了部分。GFS不能保证所有的副本每个字节都是一样的。它只保证每个数据作为一个原子单元被写过至少一次。这个是这样得出的:操作要是成功,数据必须在所有的副本上的同样的偏移处被写过。进一步,从这以后,所有的副本至少和记录一样长,所以后续的记录将被指定到更高的偏移处或者一个不同的块上,即使另一个副本成了primary。根据一致性保证,成功的record append操作的区间是已定义的。而受到干扰的区间是不一致的。
(4)快照(snapshot)
快照操作几乎在瞬间构造一个文件和目录树的副本,同时将正在进行的其他修改操作对它的影响减至最小。
我们使用copy-on-write技术来实现snapshot。当master受到一个snapshot请求时,它首先将要snapshot的文件上块上的lease。这使得任何一个向这些块写数据的操作都必须和master交互以找到拥有lease的副本。这就给master一个创建这个块的副本的机会。
副本被撤销或终止后,master在磁盘上登记执行的操作,然后复制源文件或目录树的metadata以对它的内存状态实施登记的操作。这个新创建的snapshot文件和源文件(其metadata)指向相同的块(chunk)。
Snapshot之后,客户第一次向chunk c写的时候,它发一个请求给master以找到拥有lease的副本。Master注意到chunk c的引用记数比1大,它延迟对用户的响应,选择一个chunk handle C’,然后要求每一有chunk c的副本的chunkserver创建一个块C’。每个chunkserver在本地创建chunk C’避免了网络开销。从这以后和对别的块的操作没有什么区别。
3、MASTER操作
MASTER执行所有名字空间的操作,除此之外,他还在系统范围管理数据块的复制:决定数据块的放置方案,产生新数据块并将其备份,和其他系统范围的操作协同来确保数据备份的完整性,在所有的数据块服务器之间平衡负载并收回没有使用的存储空间。
3.1 名字空间管理和加锁
与传统文件系统不同的是,GFS没有与每个目录相关的能列出其所有文件的数据结构,它也不支持别名(unix中的硬连接或符号连接),不管是对文件或是目录。GFS的名字空间逻辑上是从文件元数据到路径名映射的一个查用表。
MASTER在执行某个操作前都要获得一系列锁,例如,它要对/d1/d2…/dn/leaf执行操作,则它必须获得/d1,/d1/d2,…, /d1/d2/…/dn的读锁,/d1/d2…/dn/leaf的读锁或写锁(其中leaf可以使文件也可以是目录)。MASTER操作的并行性和数据的一致性就是通过这些锁来实现的。
3.2 备份存储放置策略
一个GFS集群文件系统可能是多层分布的。一般情况下是成千上万个文件块服务器分布于不同的机架上,而这些文件块服务器又被分布于不同机架上的客户来访问。因此,不同机架上的两台机器之间的通信可能通过一个或多个交换机。数据块冗余配置策略要达到连个目的:最大的数据可靠性和可用性,最大的网络带宽利用率。因此,如果仅仅把数据的拷贝置于不同的机器上很难满足这两个要求,必须在不同的机架上进行数据备份。这样即使整个机架被毁或是掉线,也能确保数据的正常使用。这也使数据传输,尤其是读数据,可以充分利用带宽,访问到多个机架,而写操作,则不得不涉及到更多的机架。
3.3 产生、重复制、重平衡数据块
当MASTER产生新的数据块时,如何放置新数据块,要考虑如下几个因素:(1)尽量放置在磁盘利用率低的数据块服务器上,这样,慢慢地各服务器的磁盘利用率就会达到平衡。(2)尽量控制在一个服务器上的“新创建”的次数。(3)由于上一小节讨论的原因,我们需要把数据块放置于不同的机架上。
MASTER在可用的数据块备份低于用户设定的数目时需要进行重复制。这种情况源于多种原因:服务器不可用,数据被破坏,磁盘被破坏,或者备份数目被修改。每个被需要重复制的数据块的优先级根据以下几项确定:第一是现在的数目距目标的距离,对于能阻塞用户程序的数据块,我们也提高它的优先级。最后, MASTER按照产生数据块的原则复制数据块,并把它们放到不同的机架内的服务器上。
MASTER周期性的平衡各服务器上的负载:它检查chunk分布和负载平衡,通过这种方式来填充一个新的服务器而不是把其他的内容统统放置到它上面带来大量的写数据。数据块放置的原则与上面讨论的相同,此外,MASTER还决定那些数据块要被移除,原则上他会清除那些空闲空间低于平均值的那些服务器。
3.4 垃圾收集
在一个文件被删除之后,GFS并不立即收回磁盘空间,而是等到垃圾收集程序在文件和数据块级的的检查中收回。
当一个文件被应用程序删除之后,MASTER会立即记录下这些变化,但文件所占用的资源却不会被立即收回,而是重新给文件命了一个隐藏的名字,并附上了删除的时间戳。在MASTER定期检查名字空间时,它删除超过三天(可以设定)的隐藏的文件。在此之前,可以以一个新的名字来读文件,还可以以前的名字恢复。当隐藏的文件在名字空间中被删除以后,它在内存中的元数据即被擦除,这就有效地切断了他和所有数据块的联系。
在一个相似的定期的名字空间检查中,MASTER确认孤儿数据块(不属于任何文件)并擦除他的元数据,在和MASTER的心跳信息交换中,每个服务器报告他所拥有的数据块,MASTER返回元数据不在内存的数据块,服务器即可以删除这些数据块。
3.5 过时数据的探测
在数据更新时如果服务器停机了,那么他所保存的数据备份就会过时。对每个数据块,MASTER设置了一个版本号来区别更新过的数据块和过时的数据块。
当MASTER授权一个新的lease时,他会增加数据块的版本号并会通知更新数据备份。MASTER和备份都会记录下当前的版本号,如果一个备份当时不可用,那么他的版本号不可能提高,当ChunkServer重新启动并向MASTER报告他的数据块集时,MASTER就会发现过时的数据。
MASTER在定期的垃圾收集程序中清除过时的备份,在此以前,处于效率考虑,在各客户及英大使,他会认为根本不存在过时的数据。作为另一个安全措施, MASTER在给客户及关于数据块的应答或是另外一个读取数据的服务器数据是都会带上版本信息,在操作前客户机和服务器会验证版本信息以确保得到的是最新的数据。
4、容错和诊断
4.1 高可靠性
4.1.1 快速恢复
不管如何终止服务,MASTER和数据块服务器都会在几秒钟内恢复状态和运行。实际上,我们不对正常终止和不正常终止进行区分,服务器进程都会被切断而终止。客户机和其他的服务器会经历一个小小的中断,然后它们的特定请求超时,重新连接重启的服务器,重新请求。
4.1.2 数据块备份
如上文所讨论的,每个数据块都会被备份到放到不同机架上的不同服务器上。对不同的名字空间,用户可以设置不同的备份级别。在数据块服务器掉线或是数据被破坏时,MASTER会按照需要来复制数据块。
4.1.3 MASTER备份
为确保可靠性,MASTER的状态、操作记录和检查点都在多台机器上进行了备份。一个操作只有在数据块服务器硬盘上刷新并被记录在MASTER和其备份的上之后才算是成功的。如果MASTER或是硬盘失败,系统监视器会发现并通过改变域名启动它的一个备份机,而客户机则仅仅是使用规范的名称来访问,并不会发现MASTER的改变。
4.2 数据完整性
每个数据块服务器都利用校验和来检验存储数据的完整性。原因:每个服务器随时都有发生崩溃的可能性,并且在两个服务器间比较数据块也是不现实的,同时,在两台服务器间拷贝数据并不能保证数据的一致性。
每个Chunk按64kB的大小分成块,每个块有32位的校验和,校验和和日志存储在一起,和用户数据分开。
在读数据时,服务器首先检查与被读内容相关部分的校验和,因此,服务器不会传播错误的数据。如果所检查的内容和校验和不符,服务器就会给数据请求者返回一个错误的信息,并把这个情况报告给MASTER。客户机就会读其他的服务器来获取数据,而MASTER则会从其他的拷贝来复制数据,等到一个新的拷贝完成时,MASTER就会通知报告错误的服务器删除出错的数据块。
附加写数据时的校验和计算优化了,因为这是主要的写操作。我们只是更新增加部分的校验和,即使末尾部分的校验和数据已被损坏而我们没有检查出来,新的校验和与数据会不相符,这种冲突在下次使用时将会被检查出来。
相反,如果是覆盖现有数据的写,在写以前,我们必须检查第一和最后一个数据块,然后才能执行写操作,最后计算和记录校验和。如果我们在覆盖以前不先检查首位数据块,计算出的校验和则会因为没被覆盖的数据而产生错误。
在空闲时间,服务器会检查不活跃的数据块的校验和,这样可以检查出不经常读的数据的错误。一旦错误被检查出来,服务器会拷贝一个正确的数据块来代替错误的。
4.3 诊断工具
广泛而细致的诊断日志以微小的代价换取了在问题隔离、诊断、性能分析方面起到了重大的作用。GFS服务器用日志来记录显著的事件(例如服务器停机和启动)和远程的应答。远程日志记录机器之间的请求和应答,通过收集不同机器上的日志记录,并对它们进行分析恢复,我们可以完整地重现活动的场景,并用此来进行错误分析。
6 测量
6.1 测试环境
一台主控机,两台主控机备份,16台数据块服务器,16台客户机。
每台机器:2块PIII1.4G处理器,2G内存,2块80G5400rpm的硬盘,1块100Mbps全双工网卡
19台服务器连接到一个HP2524交换机上,16台客户机俩接到领外一台交换机上,两台交换机通过1G的链路相连。

CLUCENE-0.9.10 BUG及修改方法列表

IndexWriter.cpp文件IndexWriter::close函数中,如果指定目录在退出时不关闭,则不能删除目录对象。

修改:
if ( closeDir ){
directory->close();
}
_CLDECDELETE(directory);
为
if ( closeDir ){
directory->close();
_CLDECDELETE(directory);
}
IndexWriter.cpp文件IndexWriter ::optimize函数中,如果 segmentInfo->size() == 0,则会导致异常。

修改:
flushRamSegments();
为
flushRamSegments();
if(segmentInfos->size() == 0) return;

修改IndexWriter::addIndexes(Directory** dirs)函数,因为SegmentInfos对象在析构时会删除所有对象,因此在函数退出时会导致无效的指针。

修改
// start with zero or 1 seg so optimize the current
optimize();

//Iterate through the directories
int32_t i = 0;
while ( dirs[i] != NULL ) {
// DSR: Changed SegmentInfos constructor arg (see bug discussion below).
SegmentInfos sis(false);
sis.read( dirs[i]);

for (int32_t j = 0; j < sis.size(); j++) {
/* DSR:CL_BUG:
** In CLucene 0.8.11, the next call placed a pointer to a SegmentInfo
** object from stack variable $sis into the vector this->segmentInfos.
** Then, when the call to optimize() is made just before exiting this
** function, $sis had already been deallocated (and has deleted its
** member objects), leaving dangling pointers in this->segmentInfos.
** I added a SegmentInfos constructor that allowed me to order it not
** to delete its members, invoked the new constructor form above for
** $sis, and the problem was solved. */
segmentInfos->add(sis.info(j)); // add each info
}
i++;
}
为
//Iterate through the directories
int32_t i = 0;
SegmentInfo *si;
while ( dirs[i] != NULL ) {
// DSR: Changed SegmentInfos constructor arg (see bug discussion below).
SegmentInfos sis(false);
sis.read( dirs[i]);

for (int32_t j = 0; j < sis.size(); j++) {
/* DSR:CL_BUG:
** In CLucene 0.8.11, the next call placed a pointer to a SegmentInfo
** object from stack variable $sis into the vector this->segmentInfos.
** Then, when the call to optimize() is made just before exiting this
** function, $sis had already been deallocated (and has deleted its
** member objects), leaving dangling pointers in this->segmentInfos.
** I added a SegmentInfos constructor that allowed me to order it not
** to delete its members, invoked the new constructor form above for
** $sis, and the problem was solved. */
si = sis.info(j);
segmentInfos->add( new SegmentInfo(si->name, si->docCount, si->getDir())); // add each info
}
i++;
}
修改IndexWriter.cpp文件IndexWriter::IndexWriter函数(两个),会导致内存泄漏

修改
segmentInfos (_CLNEW SegmentInfos),
为
segmentInfos (_CLNEW SegmentInfos(true)),


修改IndexWriter.cpp文件IndexWriter::addIndexes(IndexReader** readers),提高效率
修改后的函数如下:
void IndexWriter::addIndexes(IndexReader** readers){
SCOPED_LOCK_MUTEX(addIndexesReaders_LOCK);

char* mergedName = newSegmentName();
SegmentMerger* merger = _CLNEW SegmentMerger(directory, mergedName, false);

for(int i = 0; i < segmentInfos->size(); i ++) // add existing index, if any
merger->add(_CLNEW SegmentReader(segmentInfos->info(i)));

int32_t readersLength = 0;
while ( readers[readersLength] != NULL )
merger->add((SegmentReader*) readers[readersLength++]);

int32_t docCount = merger->merge(); // merge 'em

// pop old infos & add new
segmentInfos->clearto(0);
segmentInfos->add(_CLNEW SegmentInfo(mergedName, docCount, directory));

LuceneLock* lock = directory->makeLock("commit.lock");
IndexWriterLockWith with ( lock,LUCENE_COMMIT_LOCK_TIMEOUT,this,true);

LOCK_MUTEX(directory->DIR_OBJ); // in- & inter-process sync
with.run();
UNLOCK_MUTEX(directory->DIR_OBJ);

_CLDELETE(lock);
}

修改SegmentReader.cpp文件SegmentReader::files()函数,会导致删除未用的文件是不能正确删除.cfs文件。

修改
_ADD_SEGMENT(".cfs " );
为
_ADD_SEGMENT(".cfs" ); //原来的.cfs后面多了一个空格

修改util/StringIntern.h和util/StringIntern.cpp,多个线程操作类CLStringIntern时,会导致内存指针出错。方法一:对StringIntern操作时,加上互斥锁。
修改util/StringIntern.h文件
//internalise an ucs2 string and return an iterator for fast un-iteration
static __wcsintrntype::iterator internitr(const TCHAR* str CL_FILELINEPARAM);
在这行之前加上
STATIC_DEFINE_MUTEX(m_mutex);

修改util/StringIntern.cpp文件
__wcsintrntype CLStringIntern::stringPool(true);
__strintrntype CLStringIntern::stringaPool(true);
在这之后加上
DEFINE_MUTEX(CLStringIntern::m_mutex);

修改util/StringIntern.cpp文件,在所有成员函数的实现第一行之前加上:
SCOPED_LOCK_MUTEX(m_mutex);

方法二:在程序开始之前,先将所有需要用到的fieldname加入到StringIntern中,在程序结束之前将最开始加入的StringIntern删除即可。
这样在运行过程中就只会对存放StringIntern的列表进行读操作了。这种方法比第一种方法好,运行效率会提高些,不过每个程序都要写一次。

方法三:不使用stringpool,每次都直接分配内存,释放时直接把内存释放。
该方法会提高并发性能,但是会占用更多的内存,索引文档时如果放在内存的文档数比较大时,所多占用的内存数还是挺可观的。
同时,频繁的分配和释放内存也会造成内存碎片问题。
http://spaces.msn.com/chenjm/blog/

FC4的dovecot套件無法啟動

fc4下
dovecot-0.99.14-4.fc4.i386.rpm
測試正常歐
上方也有連結可以更新
不過這版本就是fc4內附的版本阿
還是樓主
本身沒從光碟安裝這套件 是另外下載的?
另外
關於sasl安裝 教學如下
以下是我從我的筆記上 擷取出來的
有疑問再發問歐 (我也不一定能解決 )
安裝前 確認防火牆打開了 110 143 25 三個
沒開的 執行 #system-config-securitylevel-tui 來打開
還有sendmail已經正確安裝
————————————-
先確認有無安裝 #代表登入root時
#rpm -qa | grep sasl
檢查以下三個rpm的安裝情形
cyrus-sasl
cyrus-sasl-md5
cyrus-sasl-plain
沒有的話 就要從光碟裡找找
編輯 /etc/mail/sendmail.mc 檔
找到
dnl TRUST_AUTH_MECH(‘EXTERNAL DIGEST-MD5?……..’)dnl
dnl define(‘confAUTH_MECHANISMS’, ‘EX……LOGIN PLAIN’)..
以上….是我偷懶懶的打字
刪去 行首的 dnl與緊接的後方空白
修改
DAEMON_OPTIONS(‘…..ADDR=0.0.0.0,….’)dnl
把127.0.0.1–>0.0.0.0
產生sendmail.cf
先把 /etc/mail/sendmail.mc 複製到 /usr/share/sendmail-cf/cf/下
終端機內 切換目錄到 /usr/share/sendmail-cf/cf/下
利用
#sh Build sendmail.cf
來產生sendmail.cf檔
將/etc/mail/sendmail.cf 更名為sendmail.cf.old
將 /usr/share/sendmail-cf/cf/ 下產生的sendmail.cf
覆蓋 /etc/mail/sendmail.cf
重新啟動服務
#/etc/rc.d/init.d/saslauthd restart
#/etc/rc.d/init.d/sendmail restart
以上我試驗過 有時要重複 重新啟動 兩次 才會正常
檢查開機時啟動 這樣就不用另外寫 script來啟動
#ntsysv

原文:http://www.fedora.tw/modules/newbb/viewtopic.php?topic_id=301&forum=1&viewmode=flat&order=DESC&start=0

sendmail 配置

作者:Christopher Shumway.
  sendmail(8) 是 FreeBSD 中的默认邮件传输代理 (MTA)。 sendmail 的任务是从邮件用户代理 (MUA) 接收邮件然后根据配置文件的定义把它们送给配置好的的寄送程序。 sendmail 也能接受网络连接,并且发送邮件到本地邮箱或者发送它到其它程序。
  sendmail 使用下列配置文件:
文件名功能
/etc/mail/access sendmail 访问数据库文件
/etc/mail/aliases 邮箱别名
/etc/mail/local-host-names sendmail 接收邮件主机列表
/etc/mail/mailer.conf 邮寄配置程序
/etc/mail/mailertable 邮件分发列表
/etc/mail/sendmail.cf sendmail的主配置文件
/etc/mail/virtusertable 虚拟用户和域列表
25.3.1 /etc/mail/access

  访问数据库定义了什么主机或者 IP 地址可以访问本地邮件服务器和它们是哪种类型的访问。主机可能会列出 OK、 REJECT、RELAY 或者简单的通过 sendmail 的出错处理程序检测一个给定的邮件错误。 主机默认列出 OK,允许传送邮件到主机, 只要邮件的最后目的地是本地主机。列出 REJECT 将拒绝所有的邮件连接。如果带有 RELAY 选项的主机将被允许通过这个邮件服务器发送邮件到任何地方。
例 25-1. 配置 sendmail 的访问许可数据库
cyberspammer.com 550 We do not accept mail from spammers FREE.STEALTH.MAILER@ 550 We do not accept mail from spammers another.source.of.spam REJECT okay.cyberspammer.com OK 128.32 RELAY
  在上面的例子中我们有 5 条记录。 与左边列表匹配的发件人受到右边列表动作的影响。前边的两个例子给出了 sendmail 的出错处理程序检测到的错误代码。当一个邮件与左边列表相匹配时,这个信息会被打印到远程主机上。 下一条记录拒绝来自 Internet 上的一个特别主机的邮件 another.source.of.spam。接下来的记录允许来自 okay.cyberspammer.com 的邮件连接, 这条记录比上面那行 cyberspammer.com 更准确。更多的准确匹配使不准确的匹配无效。最后一行允许电子邮件从主机和 128.32 开头的 IP 地址转发。这些主机将被允许通过这台邮件服务器前往其它邮件服务器发送邮件。
  当这个文件被升级的时候,您必须在 /etc/mail/ 运行 make 升级数据库。
25.3.2 /etc/mail/aliases

  别名数据库包含一个扩展到用户,程序或者其它别名的虚拟邮箱列表。 下面是一些在 /etc/mail/aliases 中使用的例子:
例 25-2. 邮件别名
root: localuser ftp-bugs: joe,eric,paul bit.bucket: /dev/null procmail: “|/usr/local/bin/procmail”
  这个文件的格式很简单; 冒号左边的邮箱名, 会被展开成右边的形式。第一个例子简单地将 root 邮箱扩展为 localuser, 之后将继续在别名数据库中进行查找。如果没有找到匹配的记录, 则邮件会被发给本地用户 localuser。第二个例子展示了一个邮件列表。 发送到 ftp-bugs 的邮件会被展开成 joe, eric 和 paul 这三个邮箱。 注意也可以通过 这样的形式来指定远程的邮箱。接下来的例子展示了如何把邮件写入到文件中, 这个例子中是 /dev/null。 最后一个例子展示了如何将邮件发给一个程序,具体而言是通过 UNIX? 管道发到 /usr/local/bin/procmail 的标准输入。
  更新此文件时, 您需要在 /etc/mail/ 中使用 make 来更新数据库。
25.3.3 /etc/mail/local-host-names

  这是一个 sendmail(8) 被接受为一个本地主机名的主机名列表。 可以放入任何 sendmail 将从那里收发邮件的域名或主机。例如,如果这个邮件服务器从域 example.com 和主机 mail.example.com 接收邮件,它的 local-host-names 文件,可以看起来象如下这样:
example.com mail.example.com
  当这个文件被升级,sendmail(8) 必须重新启动,以便更新设置。
25.3.4 /etc/mail/sendmail.cf

  sendmail的主配置文件 sendmail.cf 控制着 sendmail 的所有行为,包括从重写邮件地址到打印拒绝远程邮件服务器信息等所有事。当然,作为一个不同的角色,这个配置文件是相当复杂的,它的细节部分已经超出了本节的范围。幸运的是,这个文件对于标准的邮件服务器来说很少需要被改动。
  sendmail 主配置文件可以用 m4(1) 宏定义 sendmail 的特性和行为。它的细节请看 /usr/src/contrib/sendmail/cf/README。
  当这个文件被修改时, sendmail 必须重新启动以便对新修改生效。
25.3.5 /etc/mail/virtusertable

  virtusertable 映射虚拟域名和邮箱到真实的邮箱。这些邮箱可以是本地的、远程的、/etc/mail/aliases 中定义的别名或一个文件。
例 25-3. 虚拟域邮件映射的例子
root@example.com root postmaster@example.com postmaster@noc.example.net @example.com joe
  在上面这个例子中, 我们映射了一个域 example.com。这个文件是按照从上到下, 首个匹配的方式来处理的。 第一项将 映射到本地邮箱 root。 下一项则将 映射到位于 noc.example.net 的 postmaster。 最后,如果没有来自 example.com 的匹配, 则将使用最后一条映射,它表示将所有的其它邮件发给 example.com 域的某个人。 这样,将映射到本地信箱 joe。

strnzhcpy 中文处理(c防止汉字截断)

#include 
#include 
#include 
#include 
#include "bbs.h"

#define LEN (ARTICLE_TITLE_LEN-10)
#define PER_TEST 10000

#ifdef NEWSMTH
const char *boards[] = {"newexpress", "trafficinfo", "pielove", "joke", "picture", "advancededu", "newsoftware"};
#else
const char *boards[] = {"SYSOP"};
#endif

typedef char* (*STRNZHCPY_FUNC)(char *dest, const char *src, size_t n);

char* original_strnzhcpy(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
dest[n-1] = '\0';
return dest;
}

inline char * originalIL_strnzhcpy(char *dest, const char *src, size_t n)
{
strncpy(dest, src, n);
dest[n-1] = '\0';
return dest;
}

char* originalNP_strnzhcpy(char *dest, const char *src, size_t n)
{
int l = strlen(src);
if (n > l + 1)
n = l + 1;
memcpy(dest, src, n - 1);
dest[n - 1] = 0;
return dest;
}

char* flyriver_strnzhcpy(char *dest, const char *src, size_t n)
{
register unsigned char is_zh = 0;
register char c;
register const char *ptr = src;
register char *ptr2 = dest;

if (dest == NULL || src == NULL || n == 0)
return dest;

dest[--n] = '\0';
while ((ptr - src) < n && (c = *ptr) != '\0')
{
*ptr2 = c;
//is_zh += ((c & 0x80));
is_zh = (((c) & 0x80) & (is_zh ^ 0x80) );
ptr2++;
ptr++;
}
if ((is_zh & 0x80) != 0)
*(ptr2 - 1) = '\0';
else
*ptr2 = 0;

return dest;
}

char *dvlt_strnzhcpy(char *dest, const char *src, size_t n)
{
char *d = dest;
char flag = 0;

if (n == 0)
return dest;

for ( ; --n > 0; dest++, src++) {
if (!(*dest = *src))
break;
flag = ~flag & ((*dest) & (char)0x80);
}

if (flag)
*(dest-1) = '\0';
else
*dest = 0;

return d;
}

char* etn_strnzhcpy(char *to,const char *from,size_t size){
char *p;
unsigned int i;
size_t len;
if((len=strlen(from)) 0) c = 0;
else c = !c;
}
*dest = '\0';
if (c) *(dest-1)='\0';
return dst;
}

char *atppp_strnzhcpy(char *dest, const char *src, size_t n) {
#define COPY_ONE_BYTE \
if (*src == '\0') { n = 0; break; } \
c = (((*src) & 0x80) & (c ^ 0x80) ); \
*dest = *src; \
dest++; src++; \
n--;
register int c = 0;
register char *dst = dest;
if (n==0) return dest;
n--;
while( n > 8 ) {
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
COPY_ONE_BYTE
}
while (n > 0) {
COPY_ONE_BYTE
}
*(dest - (c>>7) )='\0';
return dst;
}
char *stiger_strnzhcpy(char *dest, const char *src, size_t n) {
register int c = 0;
register char *dst = dest;
if (n==0) return dest;
n--;
while( n > 0 && *src != '\0') {
c = (((*src) & 0x80) & (c ^ 0x80) );
*dest = *src;
dest++; src++;
n--;
}
*(dest - (c>>7) )='\0';
return dst;
}

/*
* although this is the fastest, but:
* it only works on little-endian
* it might read beyond the end of src string
*/
char *crazyatppp_strnzhcpy(char *dest, const char *src, size_t n) {
register char *dd = dest;
register int c, m;
register unsigned int si;
if (n==0) return dest;
--n;
m = n % 16;
n = n >> 4;
c = 0;
while( n > 0 ) {
#define CDW \
si = *((unsigned int*) src); \
*((unsigned int*) dest) = si; \
dest+=4; src+=4; \
if ((si & 0xff) == 0) { dest-=4; goto outofhere;} \
c = ((si & 0x80) & (c ^ 0x80) ); \
si = si >> 8; \
if ((si & 0xff) == 0) { dest-=3; goto outofhere;} \
c = ((si & 0x80) & (c ^ 0x80) ); \
si = si >> 8; \
if ((si & 0xff) == 0) { dest-=2; goto outofhere;} \
c = ((si & 0x80) & (c ^ 0x80) ); \
si = si >> 8; \
if ((si & 0xff) == 0) { dest-=1; goto outofhere;} \
c = ((si & 0x80) & (c ^ 0x80) ); \

n--;
CDW
CDW
CDW
CDW
}
#define COB \
if ((*src) == 0) goto outofhere; \
c = (((*src) & 0x80) & (c ^ 0x80) ); \
*dest = *src; \
dest++; src++;
switch(m) {
case 15: COB
case 14: COB
case 13: COB
case 12: COB
case 11: COB
case 10: COB
case 9: COB
case 8: COB
case 7: COB
case 6: COB
case 5: COB
case 4: COB
case 3: COB
case 2: COB
case 1: COB
}
outofhere:
*(dest - (c>>7) )='\0';
return dd;
}

#define TEST_FUNC(name) test_func(name##_strnzhcpy, #name)

static int range_rand(double range){
return (int)(range*rand()/((double)RAND_MAX+1));
}

void test_func(STRNZHCPY_FUNC fn, const char *func_name)
{
struct timeval tv1, tv2;
char *src;
char dest[LEN];
unsigned long t, tt=0, tt2=0, nt=0;

char path[PATHLEN];
int i,j,fd,count;
char *bname;
fileheader_t *ptr;
fileheader_t *fh;
struct boardheader *bh;
size_t fsize;
int isInline = (fn == originalIL_strnzhcpy);

#define OOPREFIX "original"
if (strncmp(func_name, OOPREFIX, strlen(OOPREFIX))) {
char ss[80], dd[80], dds[80];
char * ret;
for (j=-128;j<128>filename, DOT_DIR);
if ((fd = open(path, O_RDONLY, 0644)) < 0) {
continue;
}
if (safe_mmapfile_handle(fd, PROT_READ, MAP_SHARED, (void **)&ptr, (off_t *)&fsize)) {
fh = malloc(fsize);
memcpy(fh, ptr, fsize);
end_mmapfile((void*)ptr, fsize, -1);
} else {
close(fd);
continue;
}
close(fd);

count = fsize / sizeof(fileheader_t);
for (j=0; j
					

PHP 将 HTML 转化为 WML2005

利用 PHP 将 HTML 转化为 WML2005-07-05 @ 12:04:32 · 作者 andot · 归类于 PHP, WML

原创作品,转载请注明出处。

最近在做学校的 WAP 网站,其中通知公告、新闻等信息来源于原来的学校网站上的信息,因此就涉及到一个 HTML 转化为 WML 的问题。尽管现在的 WAP 2.0 支持 XHTML 的内容显示,但是 XHTML 语法要求比 HTML 严格,我们来源信息的 HTML 内容并不能保证符合 XHTML 的要求,而且 XHTML 相对于 WML 来说,内容的流量还是相对比较多一些的,另外仅支持 WAP 1.x 的手机设备也不支持 XHTML,因此为了支持更多的手机设备用户能够正常的浏览我们 WAP 网站,我们采用了 WML,而不是 XHTML。

对于新闻、通知、公告之类的信息,对手机设备用户来说,最关心的是文字内容,因此我们的主要工作就是按照一定的格式(比如该换行分段的地方要换行分段)来从原来的 HTML 页面里提取出纯文本信息,并将其转化为 WML。下面的代码演示了如何来实现这个步骤:

下载: html2wml.php

< ?php

/**
* @author 马秉尧
* @copyright 2005 CoolCode.CN
*/

function text2wml($content) {
// 将 WML 变量前缀"$"转义
$content = str_replace('$', '$$', $content);
// 转换特殊字符,并将 Windows/DOS 换行符(\r\n)转化为 Unix 换行符(\n)
$content = str_replace("\r\n", "\n", htmlspecialchars($content));
// 通过换行符来将各行分开进行处理(过滤空行)
$content = explode("\n", $content);
for ($i = 0; $i < count($content); $i++) {
// 过滤首尾空格
$content[$i] = trim($content[$i]);
// 如果去掉全角空格为空行,则设为空行,否则不对全角空格过滤。
if (str_replace(" ", "", $content[$i]) == "") $content[$i] = "";
}
//合并各行,转化为 WML,并过滤掉空行
$content = str_replace("\n", "", "".implode("
\n", $content)."
\n");
return $content;
}

function html2wml($content) {
// 过滤掉样式表和脚本
$content = preg_replace("/