免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1901 | 回复: 1
打印 上一主题 下一主题

[转]PHP大型Web应用入门(二) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-05-29 18:38 |只看该作者 |倒序浏览
这个函数分为两个部分,分别对应于shm和文件。首先它会检查系统是否支持共享内存(这个常量是在common.inc.php中已经设置过的),如果支持,函数会在config表中(具体应用中我也不知道你会把它放在哪里)读取所有的变量,并把它们放到一个数组中直接保存到shm里(当然实际操作不是这样简单的),如果系统不支持shm,函数会试图生成一个php文件。当再次调用这个函数时,如果shm里已经有了这个数组存在,或者已经有了这个文件存在的话(前面已经规定这个文件会被保存在tmpfs上),函数会直接返回它们的内容,不必再去读取数据库。
    这就是一个简单的cache概念。究竟什么样的数据可以并且适合被cache?这和cache的更新方式有关。cache有定时间隔更新的,有不定时更新的。定时更新的指cache存在若干时间后再次重新生成cache,通常用于统计数据,比如在线人数等。不定时更新的是指生成后就一直保持不变,直到再次检测到不存在或已过期、已损坏等情况出现,通常见于参数调用、模板编译结果等。这些数据的特点是它们都是临时的,可以被丢弃的,比如没人会在乎一个模板是否被重新编译过,除了在编译的那次执行中多占用一点时间。这批可丢弃的数据就可以被放心地保存在内存或者tmpfs中,因为它们不怕丢失,并且随时可以被重建。
    早期版本的PHPWIND论坛的cache机制是很差的,虽然它很快,但是很脆弱,一旦cache文件损坏或丢失,它不会自己去创建它,而是直接导致程序无法运行,这种只能叫做临时文件,而不能叫cache。我不知道现在的PHPWIND什么样,因为我一直没兴趣去看它……
   
    下面是shm.inc.php的源码,我不想对它加太多的注释,因为它很机械,没什么好注释的。唯一需要注意的是php的两种支持shm的方式。一种是 shmop,一种是sysv的shm,不同的是sysv只在UNIX/LINUX系统中存在,shmop更底层,只接受字符串数据。
CODE:
[Copy to clipboard]
shm_mod = 'shmop';
            }
            else {
               
$this->shm_mod = 'none';
               
$this->shm_id = false;
            }
        }
        
        else {
            if (
function_exists ('shm_attach')) {
               
$this->shm_mod = 'sysv';
            }
            elseif (
function_exists ('shmop_open')) {
               
$this->shm_mod = 'shmop';
            }
            else {
               
// No Module installed
               
$this->shm_mod = 'none';
               
$this->shm_id = false;
            }
        }
        
        if (
$this->shm_mod == 'sysv') {
            
$this->shm_id = shm_attach (ftok (__FILE__, 'g'), SHM_SIZE, 0600);
        }
        
        elseif (
$this->shm_mod == 'shmod') {
            
// if no "sysv" module installed, function "ftok())" is unavailiable.
            
$this->shm_id = shmop_open (SHM_KEY, 'n', 0600, SHM_SIZE);
        }
        
        return;
    }
   
    function
put_var ($varkey, $varval)
    {
        
// Write a value into shm
        
if ($this->shm_mod == 'sysv')
            return
shm_put_var ($this->shm_id, $varkey, $varval);
        
        elseif (
$this->shm_mod == 'shmod') {
            
// shmop is much more low-level than sysv, you need to operate every byte yourself!
            
$curr = shmop_read ($this->shm_id, 0, shmop_size ($this->shm_id));
            
$curr = base64_decode ($curr);
            
$curr = substr ($curr, 0, strpos ($curr, "这个class同时支持sysv和shmop,对于shmop,它把数据做了序列化,并用一个\0做为数据的结束。因为序列化本身并不是很快,所以有可能的话,还是sysv的shm稳定一些。
    共享内存的原本用途并不是做缓存,也不是做buffer,它是用来做进程间通信的。它可以保存临时队列,传递信号量等。我们在这里变通地用它来保存点东西,只是因为它的速度快得实在不是文件和数据库存取能比的。而且因为它的共享性,它在一段web脚本结束之后不会消失,所以它特别适合用来实现 Application变量(不用再羡慕ASP了)。
   
    下面的部分是mSession的实现,它只是模拟了session的存取过程,并对系统session进行了改进。它用了Hash目录。它的缺点是在程序结束部分还要Rewrite一下,把数据更新到session文件里,当然这个很容易被改进。
CODE:
[Copy to clipboard]
_Gen_Sid();
        
setcookie ($cookie_sess_id_varname, $sid, $sess_liftime, $cookie_path);
        
        
$sess_file = $this->_Hash_Dir($sid) . 'sess_' . $sid;
        
        if (
file_exists ($sess_file)) {
            if (!@
$fp = fopen ($sess_file, 'rb')) {
               
// Debug Info...No Log.
               
fatal_error ('Session Error...');
            }
            
            if (
0 == ($fl = filesize ($sess_file)))
               
$sess_content = '';
            
            else
               
$sess_content = fread ($fp, $fl);
        }
        else {
            if (!@
$fp = fopen ($sess_file, 'wb')) {
               
// Debug Info...No Log.
               
fatal_error ('Session Error...');
            }
            
            
$sess_content = '';
        }
        
        
fclose ($fp);
        
        
$this->sid = $sid;
        
$this->sess_file = $sess_file;
        
        
$mSession = unserialize($sess_content) or $mSession = array ();
    }
   
    function
mSession_Destroy ()
    {
        global
$mSession;
        
        
$mSession = array ();
        return @
unlink ($this->sess_file);
    }
   
    function
mSession_Rewrite ()
    {
        
// Restore Session Data into Session File
        
global $mSession;
        
        
$sess_content = serialize($mSession);
        
        if (!@
$fp = fopen ($this->sess_file, 'wb')) {
            
// Debug Info...No Log.
            
fatal_error ('Session Error...');
        }
        
        
fwrite ($fp, $sess_content);
        
fclose ($fp);
        
        return;
    }
   
    function
_Hash_Dir ($sid)
    {
        
// Hash the Session file Dir
        
        
global $user_sess_base_dir;
        
        
$sess_dir = $user_sess_base_dir . substr ($sid, 0, 1) . '/' . substr ($sid, 16, 1) . '/';
        return
$sess_dir;
    }
   
    function
_Gen_Sid ()
    {
        
// Gen an Unique Session ID
        
        
$key_1 = rand (32768, 65535);
        
$key_2 = microtime ();
        
$key_3 = sha1 (time ());
        
        
$sid = md5 ($key_1 . $key_3 . $key_2);
        
        return
$sid;
    }
   
    function
_Get_Sid ()
    {
        
// Get Current Session ID
        
global $cookie_sess_id_varname;
        
        
$sid = $_COOKIE[$cookie_sess_id_varname] ? $_COOKIE[$cookie_sess_id_varname] : FALSE;
        return
$sid;
    }
}
?>Hash目录是一种优化文件存储性能的方法。无论是Windows还是Linux,无论是NTFS还是ext3,每个目录下所能容纳的项目数是有限的。并不是不能保存,而是当项目数量过大的时候,会降低文件索引速度,所以权衡一个目录下应该保存多少文件是很必要的。保存得多了会影响性能,保存得少了会造成目录太多和空间浪费。所以当保存大批文件的时候,需要有一种算法能将文件比较均匀地“打散”在不同的子目录下以提高每一级的索引速度,这种算法就是 Hash。通常用的MD5、sha1等都可以用来做Hash目录,我的mSession里也同样使用了MD5,取得sessionID的第一位和第九位,这就构成了两级Hash路径,也就是说,系统把所有的Session文件分散到了16×16=256个子目录下。假设Linux每个目录下保存1000个文件可以获得最好的空间性能比,那么系统在理想情况下可以同时有256000个session文件在被使用。
    Hash目录还被广泛应用在备份、图库、电子邮件、静态页生成等文件密集型应用上。
   
    再来点一下我的模板类,我很懒地保留了Discuz模板函数的所有标签。一方面是我确实很懒,另一方面是我曾经试图修改Discuz,把它改成一个专用的版本,不过这是一个类,它的使用方法和Discuz函数没什么两样,都是include一个parse结果返回的文件名。
    所不同的是在处理{template}标签的时候。Discuz的处理方式是把{template}替换成再次调用模板解析函数去解析另一个模板文件,这样,模板函数可能会被调用多次,编译的结果里也会有很多include另一个模板文件Parse结果的地方。这里涉及另一个优化点——尽量少地 include文件。过多地include会带来更多的IO开销和CPU处理开销,所以我把{template}改成直接读入文件内容,然后再 parse。这样一个模板文件即使有1000个{template},编译的结果也只有一个文件。
    这个模板类用起来是如此地简单方便,更重要的是,它确实很快~~呵呵,我从来不否认我有时候也会做一些比较有用的事,哈哈:
CODE:
[Copy to clipboard]
tpl_root_dir = $root_dir;
        
        if (
$cache_root_dir != '')
            
$this->tpl_cache_root_dir = $cache_root_dir;
    }
   
    function
parse_template ($tplfile, $objfile)
    {
        
$nest = 5;
        
        if (!@
$fp = fopen ($tplfile, 'r')) {
            die (
"Current template file '" . $tplfile. " ' not found or have no access!");
        }
        
        
$template = fread ($fp, filesize ($tplfile));
        
fclose ($fp);
        
        
$var_regexp = "((\$[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)([[a-zA-Z0-9_\"'$x7f-xff]+])*)";
        
$const_regexp = "([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)";
        
        
$template = preg_replace ("/s*{templates+(.+?)}s*/ies", "file_get_contents('{$this->tpl_dir}\1.{$this->tpl_file_ext}')", $template);
        
        
$template = preg_replace ("/([nr]+)t+/s", "\1", $template);
        
$template = preg_replace ("//s", "{\1}", $template);
        
//$template = preg_replace ("/{langs+(.+?)}/ies", "languagevar('\1')", $template);
        
$template = str_replace  ("{LF}", "", $template);
        
        
$template = preg_replace ("/{(\$[a-zA-Z0-9_[]'\"$x7f-xff]+)}/s", "", $template);
        
$template = preg_replace ("/$var_regexp/es", "addquote('')", $template);
        
$template = preg_replace ("/?>/es", "addquote('')", $template);
        
        
$template = preg_replace ("/s*{evals+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        
$template = preg_replace ("/s*{elseifs+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        
$template = preg_replace ("/s*{else}s*/is", "nn", $template);
        
$template = preg_replace ("/s*{dates+(.+?)s+(.+?)}s*/ies", "stripvtags('nn', '')", $template);
        
        for(
$i = 0; $i ','n\3nn')", $template);
            
$template = preg_replace ("/s*{loops+(S+)s+(S+)s+(S+)}s*(.+?)s*{/loop}s*/ies", "stripvtags('n \3) { ?>','n\4nn')", $template);
            
$template = preg_replace ("/s*{ifs+(.+?)}s*(.+?)s*{/if}s*/ies", "stripvtags('n','n\2nn')", $template);
        }
        
        
$template = preg_replace ("/{$const_regexp}/s", "", $template);
        
$template = preg_replace ("/ ?>[nr]*parse('')/ies", $template, $arr);
        
        for ($i = 0; $i parse(' . $arr[1][$i] . ')', $template);
        }
        */
        
        
$template = str_replace ('', '', $template);
        
$template = str_replace ('', 'default_db_handle . '->num_queries; ?>', $template);
        
        
/*
        if (!@$fp = fopen($objfile, 'w')) {
            die ("Directory '" . $this->tpl_root . '/' . $this->tpl_cache_root . "' not found or have no access!");
        }
        
        flock ($fp, 3);
        fwrite ($fp, $template);
        fclose ($fp);
        */
        
        
return $template;
    }
   
    function
parse ($file)
    {
        global
$page_time, $page_time_start;
        
        
$tplfile = $this->tpl_dir . $file . '.' . $this->tpl_file_ext;
        
$objfile = $this->tpl_cache_root_dir . '/' . $this->tpl_name . '_' . $file . '.tpl.' . $this->tpl_cache_file_ext;
        
        if (!
file_exists($tplfile)) {
            
$tplfile = $this->tpl_root_dir . '/' . $this->default_tpl_name . '/' . $file.'.'.$this->tpl_file_ext;
            
$objfile = $this->tpl_cache_root_dir . '/' . $this->default_tpl_name . '_' . $file . '.tpl.' . $this->tpl_cache_file_ext;
        }
        
        if ((@
filemtime($tplfile) > @filemtime($objfile)) || (!@file_exists($objfile))) {
            
//$this->parse_template ($tplfile, $objfile);
            
if (!@$fp = fopen($objfile, 'w')) {
                die (
"Directory '" . $this->tpl_root . '/' . $this->tpl_cache_root . "' not found or have no access!");
            }
            
            
flock ($fp, 3);
            
fwrite ($fp, $this->parse_template ($tplfile, $objfile));
            
fclose ($fp);
        }
        list (
$usec, $sec) = explode (" ", microtime ());
        
$page_time_end = $usec + $sec;
        
$page_time = sprintf ("%0.6f", $page_time_end - $page_time_start);
        
        return
$objfile;
    }
   
    function
set_tpl_db_handle ($dh)
    {
        
$this->defalt_db_handle = '$' . $dh;
    }
   
    function
set_tpl_name ($name)
    {
        
$tpl_dir = $this->tpl_root_dir . '/' . $name . '/';
        if (
$name != '' && is_dir ($tpl_dir)) {
            
$this->tpl_name = $name;
            
$this->tpl_dir = str_replace ('//', '/', $tpl_dir);
        }
        else {
            
$this->tpl_name = $this->default_tpl_name;
            
$this->tpl_dir = str_replace ('//', '/', $this->tpl_root_dir . '/' . $this->tpl_name . '/');
        }
        
    }
   
    function
set_language ($langMeta)
    {
        
$langFile = $this->tpl_dir . $langMeta . '.lang';
        
clearstatcache ();
        
        if (@
is_readable ($langFile)) {
            return
$langFile;
        }
        
        elseif (@
is_readable ($this->tpl_dir . $this->default_lang . '.' . $this->tpl_lang_file_ext)) {
            
$langFile = $this->tpl_dir . $this->default_lang . '.' . $this->tpl_lang_file_ext;
            
$langMeta = $this->default_lang;
            return
$langFile;
        }
        
        elseif (@
is_readable ($this->tpl_root_dir . '/' . $this->default_tpl_name . '/' . $langMeta . '.lang')) {
            
$langFile = $this->tpl_root_dir . '/' . $this->default_tpl_name . '/' . $langMeta . '.lang';
            return
$langFile;
        }
        
        elseif (@
is_readable ($this->tpl_root_dir . '/' . $this->default_tpl_name . '/' . $this->default_lang . '.' . $this->tpl_lang_file_ext)) {
            
$langFile = $this->tpl_root_dir . '/' . $this->default_tpl_name . '/' . $this->default_lang . '.' . $this->tpl_lang_file_ext;
            
$langMeta = $this->default_lang;
            return
$langFile;
        }
        
        else
            die (
'Accept Langfile:' . $langFile . ' did not exist or has no access!');
    }
   
    function
dsp ()
    {
        global
$mSession;
        if (
$mSession['do_gzip']) {
            
$gzip_contents = ob_get_contents ();
            
ob_end_clean ();
            
            
$gzip_size = strlen ($gzip_contents);
            
$gzip_crc = crc32 ($gzip_contents);
            
            
$gzip_contents = gzcompress ($gzip_contents, 9);
            
$gzip_contents = substr ($gzip_contents, 0, strlen ($gzip_contents) - 4);
            
            echo
"x1fx8bx08x00x00x00x00x00";
            echo
$gzip_contents;
            echo
pack ('V', $gzip_crc);
            echo
pack ('V', $gzip_size);
        }
        else
            
ob_end_flush ();
    }
   
    function
get_static_html ($file)
    {
        
// Just for Test...
        
        
$static_file = '../../data/static/' . $file . '.' . $this->tpl_static_file_ext;
        if (@
$fp = fopen ($static_file, 'wb')) {
            
fwrite ($fp, ob_get_contents ());
            
fclose ($fp);
        }
        
        
ob_end_clean ();
        return;
    }
}
function
addquote ($var)
{
    return
str_replace("\\"", """, preg_replace("/[([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)]/s", "['\1']", $var));
}
   
function
stripvtags ($expr, $statement)
{
   
$expr = str_replace("\\"", """, preg_replace("//s", "\1", $expr));
   
$statement = str_replace("\\"", """, $statement);
    return
$expr . $statement;
}
   
?>后面附了一个简单的获取静态页的方法,其实也没什么用,大家都有更好的方法来生成静态页。
   
    *        *        *        *        *        *
   
    主要就是这些东西支撑起一个系统运行的必要部分。我从来不强调MVC层次,也不去讲究OOP,虽然偶尔也写一些很蹩脚的类。多年以来Pascal、C和汇编养成的习惯使我相比注意OO结构之外更注意执行效率。这次只是罗列了一些基于共享内存和tmpfs的优化方法。
    至于把什么样的数据放在tmpfs上,各位自己看着办。我把include文件、session、模板的编译结果、cache文件放在了上面。在提升IO性能的同时,它带来的另一个好处是不需要把这些文件放在web目录里,也提高了不少安全性。即使有一些文件需要放在web目录下,比如程序执行文件(废话……),也不要用奇怪的扩展名。对于config.inc.php这样的文件尤其要注意,不要使用config.inc这种文件名,很有可能你的系统忘了配置对.inc的支持,访问者可以直接在浏览器里访问config.inc就可以把这个文件下载走了,而这个文件里保存着你的数据库密码……
    走到这里,我们已经逐渐地跟上了优化的步伐,在后面的时间里,优化程序结构的同时,已经可以做好更输入地挖掘系统潜力的的准备了。将优化进行到底,挑战一下一台服务器到底能撑住多少个访问者是我近期的变态目标。不过再走下去,可能已经走出了PHP的领地,各位一定要有心理准备,因为我的C程序写得有时候比天书还乱…………hoho
    附上那个压缩/解压的类:
CODE:
[Copy to clipboard]
source_dir ? $this->source_dir : './';
        
        
$content = '' . $this->line_ret;
        
$this->_GetDirs ($source_dir);
        
$content .= '';
        
$zfp = gzopen ($this->filename, 'wb9');
        
gzwrite ($zfp, $content);
        
gzclose ($zfp);
        return;
    }
   
    function
unpack_into_dir ()
    {
        
ini_set ('memory_limit', '32M');
        
        
$target_dir = $this->target_dir ? $this->target_dir : './';
        
$zfp = gzopen ($this->filename, 'rb');
        
$content = gzread ($zfp, $this->max_filesize);
        
gzclose ($zfp);
        
$lines = explode ($this->line_ret, $content);
        while (list (
$key, $line) = each ($lines)) {
            if (
preg_match ("/(S+)/is", $line, $march)) {
               
$access_str = $march[1];
               
$item_dir = $march[2];
                if (!
is_dir ($target_dir . $item_dir)) {
                    
mkdir ($target_dir . $item_dir);
                    @
chmod ($target_dir . $item_dir, intval ($access_str, 8));
                }
            }
            
            if (
preg_match ("/(S+)/is", $line, $march)) {
               
$target_file = $march[4];
               
$access_str = $march[3];
               
$target_file_checksum = $march[2];
               
$target_filesize = $march[1];
               
                if (!@
$fp = fopen ($target_dir . $target_file, 'wb')) {
                    continue;
                }
               
                if (
false === (list ($key, $content) = each ($lines))) {   
                    continue;
                }
               
               
$file_content = base64_decode (trim ($content));
               
                if (!@
fwrite ($fp, $file_content)) {
                    continue;
                }
               
               
fclose ($fp);
               
                if (!@
md5_file ($target_file) == $target_file_checksum) {
                    
$this->error_msg = 'File : ' . $target_dir . $target_file . 'CheckSum Failed...';
                }
        
                @
chmod ($target_dir . $target_file, intval ($access_str, 8));
            
            }
        }
        return;
    }
   
    function
_GetDirs ($dirname)
    {
        global
$content;
        
        
$dh = opendir ($dirname);
        
        while (
false !== ($item = readdir ($dh))) {
            
$full_itemname = str_replace ('//', '/', $dirname . '/' . $item);
            if (
strpos ($full_itemname, $this->source_dir) === 0)
               
$pkg_itemname = substr ($full_itemname, strlen ($this->source_dir));
            else
                continue;
            
            if (
$item != '.' && $item != '..' && $item != $this->filename) {
                if (
is_dir ($full_itemname)) {
                    
$access_str = substr (decoct (fileperms ($full_itemname)), -4);
                    
$content .= "$pkg_itemname{$this->line_ret}";
                    
$this->_GetDirs ($full_itemname);
                }
               
                elseif (
is_file ($full_itemname) && is_readable ($full_itemname)) {
                    
$filesize = filesize ($full_itemname);
                    
$checksum = md5_file ($full_itemname);
                    
$access_str = substr (decoct (fileperms ($full_itemname)), -4);
                    
$content .= "$pkg_itemname{$this->line_ret}";
                    
                    @
$fp = fopen ($full_itemname, 'rb');
                    if (
$filesize > 0)
                        
$source_file_str = fread ($fp, $filesize);
                    else
                        
$source_file_str = '';
                    
$base64_str = base64_encode ($source_file_str);
                    
fclose ($fp);
                    
                    
$content .= $base64_str . $this->line_ret;
                }
            }
        }
        return;
    }
}
?>
昨天实在太困了,写的什么自己也不是很清楚~这是(一),不保证什么时候写(二)、(三)、(四)……(如果有的话)。希望大家的老板可以把省掉的买服务器的钱中的一部分发下来做奖金,嘿嘿……
//后续
Cache的目的是可以脱离数据库,所以用什么语言查询生成缓存都没关系
PHP直接连接操作MySQL的效率是很好的。C并不是很适合做Web,虽然有过很多人用C做CGI。它的好处是可以跑多线,可以底层地处理数据,可以做算法,缺点是开发效率太低。
至于C语言连接MySQL,你会发现它和PHP如此相象,因为本来就是用的同样的mysql.h...
Just Like this:
CODE:
[Copy to clipboard]

#include
#include
#include
#include
// Database name...
char        g_szHost[]          = "localhost";
UINT        g_unPort            = MYSQL_PORT;
char        g_szUserName[]      = "charlieface";
char        g_szPassword[]      = "pudgy";
char        g_szDatabase[]      = "Candy";
char        g_szSQLStatement[]  = "SELECT * chocolates";
// Entry point...
int main(int nArguments, char *pszArguments[])
{
   
// Variables...
   
MYSQL          *myDatabase      = NULL;
   
MYSQL_RES       *myResult        = NULL;
   
MYSQL_FIELD       *myField         = NULL;
   
MYSQL_ROW        myRow           = NULL;
   
UINT            unRecords       = 0;
   
UINT            unFields        = 0;
   
UINT            unIndex         = 0;
   
UINT            unFieldIndex    = 0;

   
// Initialize MySQL...
   
myDatabase = mysql_init(NULL);
   
        
// Failed...
        
if(!myDatabase)
        {
            
// Alert user...
            
printf("] Error: Unable to initialize MySQL API...n");
            
// Cleanup, abort, terminate...
            
mysql_close(myDatabase);
            
getch();
            return
0;
        }
   
// Connect to server and check for error...
   
if(mysql_real_connect(myDatabase, g_szHost, g_szUserName, g_szPassword,
                          
NULL, g_unPort, NULL, 0) != 0)
    {
        
// Alert user...
        
printf("] Error: Unable to connect to server...n");
        
// Cleanup, abort, terminate...
        
mysql_close(myDatabase);
        
getch();
        return
0;
    }
   
// Select database in server and check for error...
   
if(mysql_select_db(myDatabase, g_szDatabase) 这是Dev-Cpp的Example,看过之后,发现这几个函数~呵呵
如果你再看看PHP里mysql扩展的源码,更会发现,它到C原始lib的转换是如此直接。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/27479/showart_1947718.html

论坛徽章:
0
2 [报告]
发表于 2009-06-05 16:49 |只看该作者
好 学习
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP