嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

周梦康 发表于 2014-03-25 20171 次浏览 标签 : ZzBlogfsockopen

免费领取阿里云优惠券 我的直播 - 《PHP 进阶之路》

最近一直在搞这个,发现网上的“权威代码”也不靠谱,比如PHP终极大神“鸟哥”的代码:http://www.laruence.com/2008/04/16/98.html 看帖子下面也很多人回应不好使,但是主体代码肯定没问题。

由于自己对http协议这块了解基本为0,所以,怎么正确的模拟头信息很纠结,鸟哥的代码之所以不能正常运行,可能也是因为头信息组装出错有问题吧。

目前我写的这个模拟post提交没有用到cookie判断等,只是简单的一个提交,请求的页面能够得到post的数据。

关于http报文组装的时候空格的个数,请参考http://mengkang.net/39.html

代码如下:

function request_by_fsockopen($url,$post_data=array()){
    $url_array = parse_url($url);
    $hostname = $url_array['host'];
    $port = isset($url_array['port'])? $url_array['port'] : 80; 
    $requestPath = $url_array['path'] ."?". $url_array['query'];
    $fp = fsockopen($hostname, $port, $errno, $errstr, 10);
    if (!$fp) {
        echo "$errstr ($errno)";
        return false;
    }
    $method = "GET";
    if(!empty($post_data)){
        $method = "POST";
    }
    $header = "$method $requestPath HTTP/1.1\r\n";
    $header.="Host: $hostname\r\n";
    if(!empty($post_data)){
        $_post = strval(NULL);
        foreach($post_data as $k => $v){
                $_post[]= $k."=".urlencode($v);//必须做url转码以防模拟post提交的数据中有&符而导致post参数键值对紊乱
        }
        $_post = implode('&', $_post);
        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";//POST数据
        $header .= "Content-Length: ". strlen($_post) ."\r\n";//POST数据的长度
        $header.="Connection: Close\r\n\r\n";//长连接关闭
        $header .= $_post; //传递POST数据
    }else{
        $header.="Connection: Close\r\n\r\n";//长连接关闭
    }
    fwrite($fp, $header);
    //-----------------调试代码区间-----------------
    /*$html = '';
    while (!feof($fp)) {
        $html.=fgets($fp);
    }
    echo $html;*/
    //-----------------调试代码区间-----------------
    fclose($fp);
}

$data = array(
    'authId'=>'authcode',
    'email'=>'mengkang@php.net',
    'nickname'=>'周梦康',
    'mailBody'=>'<h3>康哥 <span style="padding:15px">某某文章</span> 有新的留言</span></h3><p>周某人 < mengkang@php.net >在评论中说:</p><div style="border-radius: 4px;margin: 10px 0 10px;border: 1px dashed #BEB0B0;padding: 8px;background: #F0F0F0;">测试回复的内容</div><p><a href="http://m.cn/github/zhoumengkang/index.php?m=Blog&a=blog&id=21#floor26">点击链接查看</a></p>'
    );
echo microtime(),"\r\n";
request_by_fsockopen('http://m.cn/github/zhoumengkang/index.php?m=Comment&a=sendEmail',$data);
echo microtime();

注意:实际使用过程中大家需要注意模拟发送数据中有&符的问题,需要先替换为其他然后在被请求端再再换回来,所以第20行的urlencode()最好是不好省略,除非确定自己的业务逻辑不需要

其中的调试代码对我调试很有帮助,在确定请求成功之前,最好去掉注释,如果请求出错,会输出访问错误的提示,这个是从http://www.cnblogs.com/mxw09/archive/2011/12/30/2307019.html 学习的。需要把调试代码注释才能变为异步,否则是需要等待的。

下面是邮件发送的代码,使用的是phpmail,添加了下请求时的验证,防止恶意请求

class CommentAction{
    //发送邮件之前的验证码
    protected $authId = 'baf09ebd312758607e0b8601598eea74';
    //简单的加密演示
    protected function auth_encode($data){
        return md5($data.'zmk');
    }
    public function sendEmail(){
        //解码已编码的URL字符串
        $data = array_map('urldecode',$_POST);
        //安全处理做
        //验证是不是我自己发送的,而非别人模拟发送的
        if($this->auth_encode($data['authId']) != $this->authId){
            return fasle;
        }
        $this->snyc_send($data);
    }
    protected function snyc_send($data){
        new MailModel($data['email'],$data['nickname'],$data['mailBody'],$data['title']);
    }
}
class MailModel {
    protected $mail;
    public function __construct($address,$nickname,$boby,$title=null) {
        require_once(dirname(__FILE__).'/../library/phpmailer/class.phpmailer.php');
        $this->mail = new PHPMailer(); //实例化
        $this->mail->IsSMTP(); // 启用SMTP
        $this->mail->Host = "smtp.exmail.qq.com"; //SMTP服务器
        $this->mail->Port = 25;  //邮件发送端口
        $this->mail->SMTPAuth   = true;  //启用SMTP认证
        $this->mail->CharSet  = "UTF-8"; //字符集
        $this->mail->Encoding = "base64"; //编码方式
        $this->mail->Username = "i@mengkang.net";  //你的邮箱
        $this->mail->Password = "xxx";  //你的密码
        $this->mail->Subject = $title ? $title : '主人,北剅轩有人造访'; //邮件标题
        $this->mail->From = "i@mengkang.net";  //发件人地址(也就是你的邮箱)
        $this->mail->FromName = "周梦康";  //发件人姓名
        $this->mail->AddAddress($address,$nickname);//添加收件人(地址,昵称)
        $this->mail->IsHTML(true);
        $this->mail->Body = $boby;//内容里面必须带博客的id链接
        if(!$this->mail->Send()) {
            echo "发送失败: " .$this->mail->ErrorInfo;
        }
    }
}

PHP异步参考资料:http://www.laruence.com/2008/04/14/318.html

嗨,老铁,欢迎来到我的博客!

如果觉得我的内容还不错的话,可以关注下我在 segmentfault.com 上的直播。我主要从事 PHP 和 Java 方面的开发,《深入 PHP 内核》作者之一。

[视频直播] PHP 进阶之路 - 亿级 pv 网站架构的技术细节与套路 直播中我将毫无保留的分享我这六年的全部工作经验和踩坑的故事,以及会穿插着一些面试中的 考点难点加分点

评论列表

回复 lixiaocheng 2014-03-25 20:57:08
照着这段话拼接出来,再用fopen打开套接字连接 就可以模拟。
回复 陈瑞 2015-05-14 11:45:31
我试着用你这种方法去异步发送邮件,apache服务器返回的是 connection refused (111) 这是什么情况,是构造的http请求信息错误,还是apache配置问题吗? 我对http请求不是很了解
回复 王志刚 2015-06-24 00:40:58
回复1楼: 这篇博客好