博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)...
阅读量:5718 次
发布时间:2019-06-18

本文共 14875 字,大约阅读时间需要 49 分钟。

测试环境配置:

  1. 环境:Windows 7系统 、PHP7.0、Apache服务器
  2. PHP框架:ThinkPHP框架(3.2)
  3. Redis数据库:测试数据回调函数:通过一个Redis的自增incr来测试异步脚本执行的次数和访问的时间(平时都是用Redis测试写日志的)
  4. 编辑器:Visual Studio Code (CLI运行环境好看点)

PHP 的命令行模式

      从版本 4.3.0 开始,PHP 提供了一种新类型的 CLI SAPI(Server Application Programming Interface,服务端应用编程端口)支持,名为 CLI,意为 Command Line Interface,即命令行接口。顾名思义,该 CLI SAPI 模块主要用作 PHP 的开发外壳应用。CLI SAPI 和其它 CLI SAPI 模块相比有很多的不同之处,我们将在本章中详细阐述。值得一提的是,CLI 和 CGI 是不同的 SAPI,尽管它们之间有很多共同的行为。 

PHP命令行(CLI)参数详解

-a               以交互式shell模式运行-c | 指定php.ini文件所在的目录-n               指定不使用php.ini文件-d foo[=bar]     定义一个INI实体,key为foo,value为'bar'-e               为调试和分析生成扩展信息-f         解释和执行文件.-h               打印帮助-i               显示PHP的基本信息-l               进行语法检查 (lint)-m               显示编译到内核的模块-r         运行PHP代码,不需要使用标签 ..?>-B   在处理输入之前先执行PHP代码-R         对输入的没一行作为PHP代码运行-F         Parse and execute  for every input line-E     Run PHP  after processing all input lines-H               Hide any passed arguments from external tools.-S : 运行内建的web服务器.-t      指定用于内建web服务器的文档根目录-s               输出HTML语法高亮的源码-v               输出PHP的版本号-w               输出去掉注释和空格的源码-z         载入Zend扩展文件 .args...          传递给要运行的脚本的参数. 当第一个参数以-开始或者是脚本是从标准输入读取的时候,使用--参数--ini            显示PHP的配置文件名--rf       显示关于函数  的信息.--rc       显示关于类  的信息.--re       显示关于扩展  的信息.--rz       显示关于Zend扩展  的信息.--ri       显示扩展  的配置信息.

启动内建web服务器,并且默认以当前目录为工作目录:

PHP 7.0.10 Development Server started at Sun Feb 26 17:48:25 2017Listening on http://localhost:8000Document root is E:\wamp64\www\cliPress Ctrl-C to quit.

查找PHP的配置文件

  在有的时候,由于服务器上软件安装比较混乱,我们可能安装了多个版本的PHP环境,这时候,如何定位我们的PHP程序使用的是那个配置文件就比较重要了。在PHP命令行参数中,提供了–ini参数,使用该参数,可以列出当前PHP的配置文件信息。

上述的服务器上我们安装了两个版本的PHP,由上可以看到,使用php –ini命令可以很方便的定位当前PHP命令将会采用哪个配置文件。

 查看类/函数/扩展信息

  通常,可以使用php –info命令或者在在web服务器上的php程序中使用函数phpinfo()显示php的信息,然后再查找相关类、扩展或者函数的信息,这样做实在是麻烦了一些。

我们可以使用下列参数更加方便的查看这些信息

--rf       显示关于函数  的信息.--rc       显示关于类  的信息.--re       显示关于扩展  的信息.--rz       显示关于Zend扩展  的信息.--ri       显示扩展  的配置信息.

查看扩展redis的配置信息

查看redis类的信息

查看函数printf的信息

语法检查

  有时候,我们只需要检查php脚本是否存在语法错误,而不需要执行它,比如在一些编辑器或者IDE中检查PHP文件是否存在语法错误。使用-l(–syntax-check)可以只对PHP文件进行语法检查:

假如此时我们的index.php中存在语法错误

PHP-CLI模式的优势及使用场合

  1. 完全支持多线程

  2. 实现定时任务

  3. 开发桌面应用就是使用PHP-CLI和GTK包

  4. linux下用php编写shell脚本

PHP 的命令行模式扩展

  其实PHP的运行环境远远不止apache和cli,如aolserver, apache, apache2filter, apache2handler, caudium, cgi (until PHP 5.3), cgi-fcgi, cli, continuity, embed, isapi, litespeed, milter, nsapi, phttpd, pi3web, roxen, thttpd, tux, and webjames.可以用php_sapi_name()这个函数去检测,这里只检测Apache服务器和Windows CMD扩展,下面编写一个cli.php文件进行测试:

Windows cmd命令行模式运行结果:

在Apache服务器模式下运行结果:

PHP 的命令行自变量

  和所有的外壳应用程序一样,PHP 的二进制文件(php.exe 文件)及其运行的 PHP 脚本能够接受一系列的参数。PHP 没有限制传送给脚本程序的参数的个数(外壳程序对命令行的字符数有限制,但通常都不会超过该限制)。传递给脚本的参数可在全局变量 $argv 中获取。该数组中下标为零的成员为脚本的名称(当 PHP 代码来自标准输入获直接用 -r 参数以命令行方式运行时,该名称为“-”)。另外,全局变量 $argc 存有 $argv 数组中成员变量的个数(而非传送给脚本程序的参数的个数)。

  PHP CLI带有两个特殊的变量,专门用来达到这个目的:一个是$argv变量,它通过命令行把传递给PHP脚本的参数保存为单独的数组元素;另一个是$argc变量,它用来保存$argv数组里元素的个数。

建立一个测试文件cli.php:

测试结果如下所示:

 

了解更多,请参考官方手册:

至此,PHP 命令行模式基本知识已介绍完毕!

下面进入实战模式:

环境介绍,Wamp环境,ThinkPHP 3.2 框架的cli模式

首先在应用程序(我这里的是:Backend)的下新建一个Library模块,在该模块中新建一个Index控制器,新建一个cmdCliTest方法,如下所示

// 定义应用目录define('APP_PATH',dirname(__FILE__).'/Backend/');
// 定义CLI运行模式运行的项目路径define('CLI_PATH',dirname(__FILE__)."\\");

 cmdCliTest方法文件内容如下所示:

//这个方法将被cli模式调用    public function cmdCliTest()    {         echo date("Y-m-d H:i:s",time()).' : ThinnPHP cli Mode Run Success:';    }

第一种,使用Apache服务器方式访问该方法,输出结果:

第二种,首先CMD到当前项目目录!!!,下面使用命令行模式输出结果:

第三种,通过Apache服务器方式运行命令行模式,这里就要涉及到一个PHP系统函数exec(),在当前控制器(Library模块index控制器)新建一个测试方法apacheToCli

//通过APache 服务器方式启动一个CLi进程    public function apacheToCli()    {        // echo CLI_PATH."cli.php Library/index/test";        echo '------------------------------------启动一个CLi进程 开始--------------------------------';        exec("E:\wamp64\bin\php\php7.0.10\php.exe E:\wamp64\www\ThinkPhpStudy\cli.php /Library/index/cmdCliTest 2>&1",$output, $return_val);        echo "

"; var_dump($output); //命令执行后生成的数组 echo "

"; var_dump("执行的状态:".$return_val); //执行的状态 echo '-----------------------------------启动一个CLi进程 结束----------------------------------'; }

 cmdCliTest方法:

//这个方法将被cli模式调用    public function cmdCliTest()    {         sleep(10); //方便我们在任务管理器查看PHP cli进程,         echo date("Y-m-d H:i:s",time()).' cmdCliTest()这个方法将被cli模式调用: ThinnPHP cli Mode Run Success:';    }

  这时候我们在Apache服务器模式下测试,可以看出在Apache服务器模式下运行的时候通过系统函数exec()成功的启动了一个php cli 进程,同时打印出了cli命令行模式执行后的结果通过第二个变量存储的$output中,打印出了返回的结果,同时第三个参数的执行装填也是:0(表示成功)

PHP的exec()函数无返回值排查方法

exec执行某命令在命令行下没有问题,但是在中就出错。这个问题99.99%与权限有关,但是exec执行的命令不会返回错误。一个技巧就是使用管道命令,假设你的exec调用如下:

exec("E:\wamp64\bin\php\php7.0.10\php.exe E:\wamp64\www\ThinkPhpStudy\cli.php /Library/index/cmdCliTest",$output, $return_val);

可以更改如下:

exec("E:\wamp64\bin\php\php7.0.10\php.exe E:\wamp64\www\ThinkPhpStudy\cli.php /Library/index/cmdCliTest 2>&1",$output, $return_val); var_dump($output); var_dump($return_val);

使用 2>&1, 命令就会输出shell执行时的错误到$output变量, 输出该变量即可分析。

注意: exec有3个参数,第一个是要执行的命令,第二个是参数是一个数组,数组的值是由第一个命令执行后生成的,第三个参数执行的状态,0表示成功,其他都表示失败。在php里面一共有三个函数可以用来执行外部命令system,exec,passthru。

PHP 执行shell脚本的返回值

exec执行一个shell 脚本:

$cmdStr = "ffmpeg/script/check_location_cut.sh {$activityid2} {$sourcefile} {$starttime} {$endtime} {$editid} {$video_desc}";exec("{$cmdStr}",$output, $sysStatus);

  第一次执行这个脚本的时候,脚本中的命令是执行成功的,但是每次回调的状态码 $sysStatus 一直是1 而不是0(表示成功),最终的原因是在shell脚本最后的返回值出现了错误:exit 1 其实在这里exit 1 表示的是错误的输出。所以在这里修改为 exit 0 则就是没有错误信息了

  exit 命令同于退出shell,并返回给定值。在shell脚本中可以终止当前脚本执行。执行exit可使shell以指定的状态值退出。若不设置状态值参数,则shell以预设值退出。状态值0代表执行成功,其他值代表执行失败。

更多:

 

PHP+Mysql批量发送邮件

关于发送邮件的见另外一篇博客:

两个方法代码(Windows 环境测试,如果是Linux测试环境的话,请看后面相关内容

//    public function apacheToCliEmail()    {        echo '------------------------------------启动一个CLi进程 开始--------------------------------';        exec("E:\wamp64\bin\php\php7.0.10\php.exe E:\wamp64\www\ThinkPhpStudy\cli.php /Library/Email/taskTable 2>&1", $output, $return_val);        echo "

"; var_dump($output); //命令执行后生成的数组 echo "

"; var_dump("执行的状态:" . $return_val); //执行的状态 echo '-----------------------------------启动一个CLi进程 结束----------------------------------'; }

命令行模式cli 需要执行的方法(命令行下为一个文件,不一定是php文件)

  //cli 命令行需要执行的php文件    public function taskTable()    {        $model = M("TaskList");        $status = 0;        $conditions = array('status' => ':status');        $result = $model->where($conditions)->bind(':status', $status)->select();        if (empty($result)) exit('没有可发送的邮件');        echo '开始发送邮件:' . "\r\n";        foreach ($result as $key => $value) {            //发送邮件            $result = send_email($value['user_email'], 'Tinywan激活邮件', "https://github.com/Tinywan");            //发送成功            if ($result['error'] == 0) {                //修改数据库字段status 的值为1                $model->where(array('id' => $value['id']))->setField('status', 1);            }            sleep(10);            //其实在这里可以添加一个状态表示没有发送成功的标记,修改数据库字段status 的值为2            //$model->where(array('id' => $value['id']))->setField('status', 2);        }        exit('发送邮件结束');    }

测试结果:

发送邮件的方法

/** * 发送邮件 * @param  array $address 需要发送的邮箱地址 发送给多个地址需要写成数组形式 * @param  string $subject 标题 * @param  string $content 内容 * @return array  放回状态吗和提示信息 */function send_email($address, $subject, $content){    $email_smtp = C('EMAIL_SMTP');    $email_username = C('EMAIL_USERNAME');    $email_password = C('EMAIL_PASSWORD');    $email_from_name = C('EMAIL_FROM_NAME');    if (empty($email_smtp) || empty($email_username) || empty($email_password) || empty($email_from_name)) {        return ["error" => 1, "message" => '邮箱请求参数不全,请检测邮箱的合法性'];    }    $phpmailer = new PHPMailer();    //     设置PHPMailer使用SMTP服务器发送Email    $phpmailer->IsSMTP();    //     设置为html格式    $phpmailer->IsHTML(true);    //     设置邮件的字符编码'    $phpmailer->CharSet = 'UTF-8';    // 设置SMTP服务器。    $phpmailer->Host = $email_smtp;    // 设置为"需要验证"    $phpmailer->SMTPAuth = true;    // 设置用户名    $phpmailer->Username = $email_username;    // 设置密码    $phpmailer->Password = $email_password;    // 设置邮件头的From字段。    $phpmailer->From = $email_username;    // 设置发件人名字    $phpmailer->FromName = $email_from_name;    // 添加收件人地址,可以多次使用来添加多个收件人    if (is_array($address)) {        foreach ($address as $addressv) {            //验证邮件地址,非邮箱地址返回为false            if(false === filter_var($address,FILTER_VALIDATE_EMAIL)){                return ["error" => 1, "message" => '邮箱格式错误'];            }            $phpmailer->AddAddress($addressv);        }    } else {        //验证邮件地址,非邮箱地址返回为false        if(false === filter_var($address,FILTER_VALIDATE_EMAIL)){            return ["error" => 1, "message" => '邮箱格式错误'];        }        $phpmailer->AddAddress($address);    }    // 设置邮件标题    $phpmailer->Subject = $subject;    // 设置邮件正文,这里最好修改为这个,不是boby    $phpmailer->MsgHTML($content);    // 发送邮件。    if (!$phpmailer->Send()) {        return ["error" => 1, "message" => $phpmailer->ErrorInfo];    }    return ["error" => 0];}

PHP 异步执行脚本

  这里说的异步执行是让PHP脚本在后台挂起一个执行具体操作的脚本,主脚本退出后,挂起的脚本还能继续执行。比如执行某些耗时操作或可以并行执行的操作,可以采用php异步执行的方式。主脚本和子脚本的通讯可以采用外部文件或memcached的方式。原理就是通过exec或system来执行一个外部命令。注意:在这里所述的是针对Linux环境(不是Windows 环境哦!)

  在Linux下要让一个脚本挂在后台执行可以在命令的结尾加上一个 “&” 符号,有时候这还不够,需要借助nohup命令,这个命令下面有专门的介绍

  Cli环境和Web环境执行的操作还不太一样。先来说CLI环境,这里需要用上nohup和&,同时还要把指定输出,如果不想要输出结果,可以把输出定向到/dev/null中。现在来做一个测试,假设在一个目录中有main.php、sub1.php和sub2.php,其中sub1和sub2内容一样都让sleep函数暂停一段时间。代码如下:

//main.php
./tmp.log &'; exec($cmd); $cmd = 'nohup php ./sub1.php >/dev/null &'; exec($cmd);?>//sub1.php sub2.php

  上述文件中main.php是作为主脚本,在命令行中执行php main.php,可以看到main.php脚本很快就执行完并退出。在使用ps aux | grep sub命令搜索进程,应该可以在后台看到上述的两个子脚本,说明成功挂起了子脚本。

  在Web环境下,执行php脚本都是Web服务器开启的cgi进程来处理,只要脚本不退出,就会一直占有该cgi进程,当启动的所有cgi进程都被占用完后就不能在处理新的请求。所以对那些可能会很费时的脚本,可以采用异步的方式。启动子脚本的方式和CLI差不多,必须要使用&和指定输出(只好是定向到/dev/null),但是不能使用nohup。例如:

/dev/null &'; exec($cmd); $cmd = 'php PATH_TO_SUB1/sub2.php >/dev/null &'; exec($cmd);?>

当在浏览器中访问该脚本文件,可以看到浏览器里面响应完成,同时使用ps命令查看后台可以看到sub1和sub2脚本。注意上述例子中如果php命令不在PATH中,需要指定命令完整的路径。推荐使用完整路径,特别是在Web下。

nohup命令及其输出文件

  今天在linux上部署wdt程序,在SSH客户端执行./start-dishi.sh,启动成功,在关闭SSH客户端后,运行的程序也同时终止了,怎样才能保证在推出SSH客户端后程序能一直执行呢?通过网上查找资料,发现需要使用nohup命令。完美解决方案:nohup ./start-dishi.sh >output 2>&1 & ,现对上面的命令进行下解释:

  • 用途:不挂断地运行命令。
  • 语法:nohup Command [ Arg ... ] [ & ]
  • 描述:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部。

操作系统中有三个常用的流:

  • 0:标准输入流 stdin
  • 1:标准输出流 stdout
  • 2:标准错误流 stderr

  一般当我们用 > console.txt,实际是 1>console.txt的省略用法;< console.txt ,实际是 0 < console.txt的省略用法。

测试案例结果:

ww@iZ232eoxo41Z:~/tinywan $ nohup ./start-dishi.sh >output 2>&1 &

说明:

  1. 带&的命令行,即使terminal(终端)关闭,或者电脑死机程序依然运行(前提是你把程序递交到服务器上)。
  2. 2>&1的意思是把标准错误(2)重定向到标准输出中(1),而标准输出又导入文件output里面,所以结果是标准错误和标准输出都导入文件output里面了。 至于为什么需要将标准错误重定向到标准输出的原因,那就归结为标准错误没有缓冲区,stdout有。这就会导致 >output 2>output 文件output被两次打开,而stdout和stderr将会竞争覆盖,这肯定不是我门想要的。
  3. /dev/null文件的作用,这是一个无底洞,任何东西都可以定向到这里,但是却无法打开。 所以一般很大的stdou和stderr当你不关心的时候可以利用stdout和stderr定向到这里:./command.sh >/dev/null 2>&1 

注意:这就是为什么有人会写成: nohup ./command.sh >output 2>output出错的原因了

 

=============在Linux环境下PHP 异步执行脚本发送事件通知消息(实践)===============

  需求(思想中心):很多客户会担心消息丢了怎么办,比如客户的服务器宕机了一下会儿,消息会不会丢失呢?为了保证消息可靠性保证机制是基于简单重传实现的,即:如果一条通知消息没有成功发送到您指定的回调URL,反复重试100次(自定义次数)。那怎么确认消息是已经送达您的服务器(客户端)了呢?这里是需要您的协助的:当您的服务器成功收到一条http事件通知消息时,例如在请求的URl中请求成功的时候返回一个字段,0表示客户端服务器已经接受到服务器发送的事件通知消息了,这时候脚本符合条件直接退出脚本执行即可(也就是后台运行的Cli php 后台程序)

测试环境配置:

  1. 环境:Linux(ubuntu 14.04) ,必须的安装好PHP的WEB环境和CLI环境
  2. PHP框架:Phalcon框架(3.0)
  3. Redis数据库:测试数据回调函数:通过一个Redis的自增incr来测试异步脚本执行的次数和访问的时间(平时都是用Redis测试写日志的)

Server 服务器端的执行代码

  //CLI模式,模拟队列的形成    public function listExecAction()    {        $streamName = 4001488177666;        $fileSize = 123.001;        $duration = 123;        $video_url = "http://ip/data/{
$streamName}/video/{
$streamName}.mp4"; $callBackUrl = "http://ip/openapi/videoCallbackFunction?streamName={
$streamName}&fileSize={
$fileSize}&duration={
$duration}&video_url={
$video_url}"; echo '------------------------------------启动一个CLi进程 开始--------------------------------' . date("Y-m-d H:i:s"); exec("/usr/bin/php /home/www/tinywan/cli_demo.php '{
$callBackUrl}' >/dev/null 2>&1 &"); echo "

"; echo '-----------------------------------启动一个CLi进程 结束----------------------------------' . date("Y-m-d H:i:s"); die; }

  Cli.php执行脚本代码,通过使用CURL的PHP扩展完成一个HTTP请求(GET方式),默认最大发送请求1000次,如果客户端已经接受到事件通知信息了,则立马跳出while循环,当然了后台脚本也就会执行结束了,如果客户端服务器返回状态JSON字符串值为0,则表示客户服务器成功的接受到了事件通知信息,这时候符合第二个条件,则立马跳出循环,停止后台脚本的执行。

1000) { break; } //解析JSON字符串为数组 $res = json_decode($response, true); //如果客户端返回数据为0 则表示接收到数据了 if ($res[0] == '0') { break; } continue;}exit(1);

  Client客户服务器 模拟一个客户端程序代码(我这里是测试回调的),以下代码用来接受PHP异步执行的脚本返回的参数,同时存储在Redis数据库中去,这里做了一个自增字段VideoId,用来记录脚本执行的次数(当然你也可以直接在命令行里面写一个sleep()函数用来做测试的)

/**     * 默认的录像回调函数     */    public function videoCallbackFunctionAction()    {        $this->view->disable();        $streamName = $this->request->getQuery("streamName");        $videoId = $this->request->getQuery("videoId");        $endTime = $this->request->getQuery("endTime");        $fileName = $this->request->getQuery("fileName");        $baseName = $this->request->getQuery("baseName");        $fileSize = $this->request->getQuery("fileSize");        $duration = $this->request->getQuery("duration");        $redis = $this->Redis();        $redis->select(8);        $incrId = $redis->incr('videoIncrId');        $redis->hMset('videoCallback:' . $incrId, [            'streamName' => $streamName,            'fileName' => $fileName,            'baseName' => $baseName,            'fileSize' => $fileSize,            'time' => date("Y-m-d H:i:s")        ]);    }

=======================================第一次调试=========================================

echo '------------------------------------启动一个CLi进程 开始--------------------------------' . date("Y-m-d H:i:s");exec("nohup /usr/bin/php /home/www/tinywan/cli_demo.php '{
$callBackUrl}' >/dev/null 2>&1 &");echo "

";echo '-----------------------------------启动一个CLi进程 结束----------------------------------' . date("Y-m-d H:i:s");

1、测试数据之前先清空Redis数据库数据(命令:FlaushDB ,清空当前数据库的所有key键):

2、在浏览器刷新执行该脚本程序:

3、通过: ps -aux | grep php 查看PHP进程

4、查看Redis数据库信息:

5、通过以上可以看出。WEB页面并没有一直等待客户端服务器的相应,而是立马结束掉,而同时PHP脚本程序在后台运行,知道跳出循环结束

6、查看PHP后台基本执行时间为3分钟左右!

 

================第二次调试====================修改代码程序:再次调试

1、测试数据之前先清空Redis数据库数据(命令:FlaushDB ,清空当前数据库的所有key键):

2、在浏览器刷新执行该脚本程序:

3、通过: ps -aux | grep php 查看PHP进程

 

4、查看Redis数据库信息:

5、通过以上可以看出。WEB页面并没有一直等待客户端服务器的相应,而是立马结束掉,而同时PHP 脚本很快就执行完并退出,立马跳出循环结束(满足条件:$res[0] == 0,客户端服务器返回信息)

6、查看PHP后台基本执行时间为不到1分钟

===============第三次调试====================

说到这里可能有点怀疑怎么没看到PHP后台进程呢!好,下来我们sleep(10)函数暂停10秒时间,继续测试一下不就知道了,哈哈!

$count = 0;sleep(10);while (true) {

1、步骤同上,清楚Redis数据库数据

 

2、WEB页面执行结果

3、PHP后台异步脚本程序

4、Redis数据库记录数据

 

测试完毕,可以的,小伙子!棒棒哒!!!!!2017-02-28 16:20:48

 

转载地址:http://shqgt.baihongyu.com/

你可能感兴趣的文章
“位运算”在实际项目中的应用,保证你能学到东西!
查看>>
CF240E Road Repairs
查看>>
HDU 1043 Eight (A* + HASH + 康托展开)
查看>>
【转】Python 可视化神器-Plotly Express
查看>>
计算机网络原理笔记-停止等待协议
查看>>
topcoder srm 662 div1
查看>>
Java基础之静态变量
查看>>
更换好的yum源
查看>>
NET牛人应该知道些什么?
查看>>
MS SQL处理“分子分母都有除法式”方法,避免devide by zero error?A:COALESCE函数
查看>>
uitabbar 标题设置 button text attributes only respected for UIControlStateNormal
查看>>
[Asp.Net web api]基于自定义Filter的安全认证
查看>>
用户体验报告——脉脉
查看>>
cxPivotGrid导出数据
查看>>
xmpp登录(2)
查看>>
求解矩阵特征值及特征向量
查看>>
matlab编程如何换行
查看>>
stm32f1的IO,推挽与开漏
查看>>
phpcms首页调用会员头像及金钱、积分等信息,并按照积分点数排列
查看>>
关于大XML文件与大节点处理(System.Xml.XmlTextReader)
查看>>