用Socket发送电子邮件
http://pythonrecord.51.net/bin/content.php3?src=1&url=doc2000082201用Socket发送电子邮件--续篇
http://pythonrecord.51.net/bin/content.php3?src=1&url=doc2001030101希望这两篇文章对你有帮助,其中“用Socket发送电子邮件--续篇”介绍了如何在有认证功能的smtp服务上发邮件。

解决方案 »

  1.   

    谢谢webmin(webmin)!
    不过有没有别的方法了呀,简洁一些的
      

  2.   

    刚才把近段时间的帖子都看了一下,就剩下这个没结了
    发现wsrboy(wsrboy) (  ) 信誉:100 好像很强
    那么麻烦帮个忙把
    让我能把这个贴子结掉把
      

  3.   

    我回来了,呵呵
    按照上面说的方法我试了一下,有点问题
    里面有个成员函数如下
    function do_command($command, $code)
     {
      $this->lastact=$command;
      $this->show_debug($this->lastact, "out");
      fputs ( $this->fp, $this->lastact );
            while(true)
            {
                $this->lastmessage=fgets($this->fp,512);
                $this->show_debug($this->lastmessage, "in");
                if(($this->lastmessage[3]==' ') or (empty($this->lastmessage)))
                break;
            }
    ......
        我调试了一下,发现在调用do_command函数,执行$this->lastmessage=fgets($this->fp,512);这句时,一直执行不下去,也就是说,无法从服务器返回消息。而我在telnet方式下,响应是正确的。而其他参数,我也echo出来看过了,都是正确的。
    不知道哪位大虾用过,知道这是怎么回事?先谢了ps:只要能解决,要多少分都无所谓。:)
      

  4.   

    你的PHP服务开放了SOCKed功能吗?不过在这里我建议你再一个不需要SMTP认证的服务将你原来的那个SMTP服务替换就可以了。虽说现在在网上找不需要SMTP认证的服务器比较难找但是总是还是可以找到的,只要你认真的找找看,我现在使用的就是一个不需要SMTP认证的服务。不过老实说我不太想将它告诉给你,以免对方将修改,不好意思
      

  5.   

    :) 电信提供的SMTP一般来说是不用验证的, 你找找你们当地的电信的SMTP看看.
      

  6.   

    我这里刚刚好有这样的类库,可以用smtp验证的.
    不过不是太完善,凑合用着吧...可以满足一般要求的了...
    我在新网的smtp上试过可以的.=============inc/smtp_mail.class.php=================
    //这个是用来发smtp邮件的类
    //需要和另一个邮件编码类一起使用.
    <?php
    class smtp_mail{
    var $fp=false;
    var $lastmsg="";
    var $authMode=false;
    var $debugMode=false;
    var $authLoginName;
    var $authLoginPassword; function read_line(){
    $ret=false;
    $line=fgets($this->fp,1024);
    if($this->debugMode){
    echo "RESPONSE: $line <br>";
    }
    if(ereg("^([0-9]+).(.*)$",$line,$data)){
    $recv_code=$data[1];
    $recv_msg=$data[2];
    $ret=array($recv_code,$recv_msg);
    }
    return $ret;
    } function dialogue($code,$cmd){
    $ret=true;
    if($this->debugMode){
    echo "CWD: $cmd <br>";
    }
    fwrite($this->fp,$cmd."\r\n");
    $line=$this->read_line($this->fp);
    if($line==false){
    $ret=false;
    $this->lastmsg="";
    }
    else{
    $this->lastmsg="$line[0] $line[1]";
    if($line[0]!=$code){
    $ret=false;
    }
    return $ret;
    }
    } function error_message(){
    echo "SMTP Protocol failure (".$this->lastmsg.").<br>";
    } function crlf_encode($data){
    $data.="\n";
    $data=str_replace("\n","\r\n",str_replace("\r","",$data));
    $data=str_replace("\n.\r\n","\n. \r\n",$data);
    return $data;
    } function handle_email($from,$to,$data){
    $rcpts=explode(",",$to);
    $err=false;
        if(!$this->dialogue(250,"HELO")){
    $err=true;
    }
    if($this->authMode){
    if(!$this->dialogue(334,"AUTH LOGIN")) {
    $err=true;
    }
    if(!$this->dialogue(334,base64_encode($this->authLoginName))){
    $err=true;
    }
    if(!$this->dialogue(235,base64_encode($this->authLoginPassword))){
    $err=true;
    }
    }
    if(!$this->dialogue(250,"MAIL FROM: $from") ){
    $err=true;
    }
    for($i=0;!$err&&$i<count($rcpts);$i++){
    if(!$this->dialogue(250,"RCPT TO: $rcpts[$i]")){
    $err=true;
    }
    }
    if($err||!$this->dialogue(354,"DATA")||!fwrite($this->fp,$data)||!$this->dialogue(250,"\r\n.")||!$this->dialogue(221,"QUIT") ){
    $err=true;
    }
    if($err){
    $this->error_message();
    }
    return !$err;
    } function connect($hostname){
    if($this->debugMode){
    echo "CONNECTING $hostname ...";
    }
    $ret=false;
    $this->fp=fsockopen($hostname,25);
    if($this->fp){
    $ret=true;
    if($this->debugMode){
    echo "Succeed!<br>";
    }
    }
    else{
    if($this->debugMode){
    echo "Failed!<br>";
    }
    }
    return $ret;
    } function send_email($hostname,$from,$to,$data,$crlf_encode=1){
    if(!$this->connect($hostname)){
    echo "cannot open connection!<br>\n";
    return false;
    }
    $line=$this->read_line();
    $ret=false;
    if($line&&$line[0]=="220"){
    if($crlf_encode){
    $data=$this->crlf_encode($data);
    }
    $ret=$this->handle_email($from,$to,$data);
    }
    else{
    $this->error_message();
    }
    fclose($this->fp);
    return $ret;
    }
    };
    ?>=================inc/text_mail.class.php==========
    //这个是把邮件编码的类.
    //这个类没有对邮件进行任何编码,只是普通的text_mail,把邮件头等东西加上去而已.
    <?php
    class text_mail{
    var $reply;
    var $to;
    var $from;
    var $subject;
    var $body; function text_mail(){
    $this->to="";
    $this->from="";
    $this->subject="";
    $this->body="";
    $this->reply="";
    } function get_mail(){
    $mail="From: $this->from \r\n";
    $mail.="Reply-To: $this->reply \r\n";
    $mail.="To: $this->to \r\n";
    $mail.="Subject: $this->subject \r\n";
    $mail.="Mime-verson:1.0 \r\n";
    $mail.="Content-Type: text/plain;charset=\"GB2312\"\r\n";
    $mail.="Content-Transfer-Encoding: 8bit\r\n";
    $mail.="\r\n";
    $mail.=str_replace("\n.\r\n","\n. \r\n",$this->body);
    return $mail;
    }
    };
    ?>=================== testmail.php =====================
    //测试用的文件
    <?php
    //include "inc/mime_mail.php"; 这个类是把邮件进行mime编码的
    include "inc/text_mail.php";
    include "inc/smtp_mail.php";$smtp_server="mail.eccreation.com";  //smtp地址
    $from="[email protected]";  // 发信人地址
    $to="[email protected]"; // 收信人地址
    $subject="test!!!!!";  //题目
    $body="test only!  
    fdsafsdajfkljdklsfsda
    fds
    fdsaf
    dsfsdafds
    f"; //内容$mail=new text_mail;  //初始化编码类库
    $mail->from=$from;
    $mail->to=$to;
    $mail->subject=$subject;
    $mail->body=$body;
    $mail->reply=$from;
    $data=$mail->get_mail();    //返回编码邮件信体.
    $smtp=new smtp_mail;   //初始化邮件发送类库
    $smtp->authMode=true;  //需要smtp验证,把验证模式打开
    $smtp->authLoginName="xxxxxxx";  // 验证用户名
    $smtp->authLoginPassword="*******";  //验证密码
    $smtp->send_email($smtp_server,$from,$to,$data);  //发送邮件
    ?>
      

  7.   

    谢谢各位的关注
    我这里的情况是这样的,用来发信的这个信箱必须是公司里的一个信箱,而公司的smtp是要验证的,所以to bamboo789(Bamboo):没关系,你说的SOCKed功能是在php.ini中的哪一项配置?我怎么找不到?to pcdreama(峰幻):谢了,我试试看
      

  8.   

    to pcdreama(峰幻):
    我试了一下,还是有点问题,首先,这句写的不对,
    if(!$this->dialogue(250,"HELO")),应该是
    if(!$this->dialogue(250,"EHLO")),否则的话,是无法使用 AUTH LOGIN 命令的。然后,在这句中
    if(!$this->dialogue(334,"AUTH LOGIN")) {
       $err=true;
    }
    调用dialogue的结果,服务器返回的不是期望的 334 VXNlcm5hbWU6 
    而是 250-TURN 
    请问知道这是怎么回事吗?
      

  9.   

    to pcdreama(峰幻):
    我试了一下,还是有点问题,首先,这句写的不对,
    if(!$this->dialogue(250,"HELO")),应该是
    if(!$this->dialogue(250,"EHLO")),否则的话,是无法使用 AUTH LOGIN 命令的。然后,在这句中
    if(!$this->dialogue(334,"AUTH LOGIN")) {
       $err=true;
    }
    调用dialogue的结果,服务器返回的不是期望的 334 VXNlcm5hbWU6 
    而是 250-TURN 
    请问知道这是怎么回事吗?
      

  10.   

    没有啊,我用新网的服务器试过是没问题的.
    在你的服务器不行,可能是协议有些不正确吧.因为服务器的验证有很多种.
    我这里的这种是使用base64对帐号密码编码,AUTH LOGIN后返回的
    334 VXNlcm5hbWU6是user的意思,然后下面那个是提示输入password.
    第一行那里,如果协议用EHLO我记得就不能正确运行了...这个我也觉得奇怪
    根据协议,应该连接后,首先用ehlo列出可以用的协议,然后根据这些协议再选择某个连接方法.但是我用这些fgets这类函数只能取得一行反馈信息.而用freads这类函数则不能使用.所以我这个类只好只使用AUTH LOGIN的方法咯.
    你可以自己用telnet模拟一下协议的过程,然后根据你那台服务器的特点修改一下我的程序就ok了.以下是结果:
    CONNECTING mail.eccreation.com ...Succeed!
    RESPONSE: 220 mail-g1.chinadns.com ESMTP 
    CWD: HELO 
    RESPONSE: 250 mail-g1.chinadns.com 
    CWD: AUTH LOGIN 
    RESPONSE: 334 VXNlcm5hbWU6 
    CWD: cGNkcmVhb*********VhdGlvbi5jb20= 
    RESPONSE: 334 UGFzc3dvcmQ6 
    CWD: bm****** 
    RESPONSE: 235 go ahead 
    CWD: MAIL FROM: [email protected] 
    RESPONSE: 250 ok 
    CWD: RCPT TO: [email protected] 
    RESPONSE: 250 ok 
    CWD: DATA 
    RESPONSE: 354 go ahead 
    CWD: . 
    RESPONSE: 250 ok 1026447969 qp 30124 
    CWD: QUIT 
    RESPONSE: 221 mail-g1.chinadns.com 
      

  11.   

    我早上忘了说了,我在telnet下模拟是完全正常的,步骤一模一样,换到PHP下就不行了,
    奇怪就奇怪在这里
      

  12.   

    我早上忘了说了,我在telnet下模拟是完全正常的,步骤一模一样,换到PHP下就不行了,
    奇怪就奇怪在这里
      

  13.   

    我在telnet下,输入base64编码过的用户名与密码,就可以通过
    而在PHP中,输入 AUTH LOGIN 之后,服务器返回了 250-TURN 
    而不是编码过的 username: ,根本不让我继续,我倒!
      

  14.   

    那试过用HELO进行握手协议没有?
    还有把反馈信息贴出来研究研究~~
      

  15.   

    当然试过了,上面已经说了,如果使用HELO,是不行的,结果如下:
    【CWD】: HELO 
    RESPONSE: 250 hsexchange.hs.handsome.com.cn Hello [192.168.60.13] 
    【CWD】: AUTH LOGIN 
    RESPONSE: 503 5.5.2 Send hello first. 
    --- Failed here !!!
    【CWD】: Y2hlbmp4 
    RESPONSE: 500 5.3.3 Unrecognized command 
    --- Failed here !!!
    如果使用HELO握手,那么在发送AUTH LOGIN后,服务器返回Send hello first,这显然不对,而且在发送username之后,返回Unrecognized command。
    所以,应该不是使用HELO。如果使用EHLO,结果如下:
    【CWD】: EHLO 
    RESPONSE: 250-hsexchange.hs.handsome.com.cn Hello [192.168.60.13] 
    【CWD】: AUTH LOGIN 
    RESPONSE: 250-TURN 
    --- Failed here !!!
    【CWD】: Y2hlbmp4 
    RESPONSE: 250-ATRN 
    --- Failed here !!!
    【CWD】: d2****p5 
    RESPONSE: 250-SIZE 10240000 
    --- Failed here !!!第3、4行我希望看到的是
    【CWD】: AUTH LOGIN 
    RESPONSE: 334-VXNlcm5hbWU6 
    可它却是 250-TURN,不解
      

  16.   

    我在我们租的空间(新网的smtp服务器)开了一个测试帐号,
    验证id: [email protected]  (记住加后面@xxxxxx.xxx那部分)
    验证pw: test
    你可以用这个帐号测试一下.
    你那个服务器能不能开个测试帐号来调试一下?
    我也想知道为啥会这样.
      

  17.   

    好的,我试试看
    smtp服务器是这样的么:mail.eccreation.com我这里的测试账号:
    username: test
    password: 123456
      

  18.   

    有个简单的办法就是,让你的本地计算机不需要验证,如果你用的是qmail就可以在smtp.rules里设置
    127.allow,RELAYCLIENT=""
    这样你在本地计算机上运行mail就不需要验证了
      

  19.   

    你那个smtp服务器地址是什么?
      

  20.   

    给我你的信箱,我给你个发mail的类,是可以通过smtp认证的。
      

  21.   

    我这里用的是Exchange 2002,不是qmailsmtp服务器是:mail.handsome.com.cnto:soniclee(封寒月)
    好啊,参考一下,mailto:[email protected]
      

  22.   

    问题解决了!哈哈~新的程序 smtp_mail.class.php and testmail.php===============inc/smtp_mail.class.php==============
    <?php
    class smtp_mail{
    var $fp=false;
    var $lastmsg="";
    var $authMode=false;
    var $authLastLine;
    var $debugMode=false;
    var $authLoginName;
    var $authLoginPassword; function read_line(){
    $ret=false;
    $line=fgets($this->fp,1024);
    if($this->debugMode){
    echo "RESPONSE: $line <br>";
    flush();
    }
    if(ereg("^([0-9]+).(.*)$",$line,$data)){
    $recv_code=$data[1];
    $recv_msg=$data[2];
    $ret=array($recv_code,$recv_msg);
    }
    return $ret;
    } function dialogue($code,$cmd,$response=false){
    $ret=true;
    if($this->debugMode){
    echo "CWD: $cmd <br>";
    flush();
    }
    fwrite($this->fp,$cmd."\r\n");
    $line=$this->read_line();
    if($line==false){
    $ret=false;
    $this->lastmsg="";
    }
    else{
    $this->lastmsg="$line[0] $line[1]";
    if($line[0]!=$code){
    $ret=false;
    return $ret;
    }
    if($response!==false){
    $response.="\r\n";
    while($line[1]!=$response){
    $line=$this->read_line();
    // $this->lastmsg.="<br>$line[0] $line[1]";
    if($line[0]!=$code){
    $ret=false;
    return $ret;
    }
    }
    }
    return $ret;
    }
    } function error_message(){
    echo "SMTP Protocol failure (".$this->lastmsg.").<br>";
    flush();
    } function crlf_encode($data){
    $data.="\n";
    $data=str_replace("\n","\r\n",str_replace("\r","",$data));
    $data=str_replace("\n.\r\n","\n. \r\n",$data);
    return $data;
    } function handle_email($from,$to,$data){
    $rcpts=explode(",",$to);
    $err=false;
        if(!$this->dialogue(250,"HELO")){
    $err=true;
    }
        if(!$this->dialogue(250,"EHLO",$this->authLastLine)){
    $err=true;
    }
    if($this->authMode){
    if(!$this->dialogue(334,"AUTH LOGIN")) {
    $err=true;
    }
    if(!$this->dialogue(334,base64_encode($this->authLoginName))){
    $err=true;
    }
    if(!$this->dialogue(235,base64_encode($this->authLoginPassword))){
    $err=true;
    }
    }
    if(!$this->dialogue(250,"MAIL FROM: $from") ){
    $err=true;
    }
    for($i=0;!$err&&$i<count($rcpts);$i++){
    if(!$this->dialogue(250,"RCPT TO: $rcpts[$i]")){
    $err=true;
    }
    }
    if($err||!$this->dialogue(354,"DATA")||!fwrite($this->fp,$data)||!$this->dialogue(250,"\r\n.")||!$this->dialogue(221,"QUIT") ){
    $err=true;
    }
    if($err){
    $this->error_message();
    }
    return !$err;
    } function connect($hostname){
    if($this->debugMode){
    echo "CONNECTING $hostname ...";
    }
    $ret=false;
    $this->fp=fsockopen($hostname,25);
    if($this->fp){
    $ret=true;
    if($this->debugMode){
    echo "Succeed!<br>";
    flush();
    }
    }
    else{
    if($this->debugMode){
    echo "Failed!<br>";
    flush();
    }
    }
    return $ret;
    } function send_email($hostname,$from,$to,$data,$crlf_encode=1){
    if(!$this->connect($hostname)){
    echo "cannot open connection!<br>\n";
    flush();
    return false;
    }
    $line=$this->read_line();
    $ret=false;
    if($line&&$line[0]=="220"){
    if($crlf_encode){
    $data=$this->crlf_encode($data);
    }
    $ret=$this->handle_email($from,$to,$data);
    }
    else{
    $this->error_message();
    }
    fclose($this->fp);
    return $ret;
    }
    };
    ?>===================testmail.php======================
    <?php
    //include "inc/mime_mail.php";
    set_time_limit(9999);
    include "inc/text_mail.class.php";
    include "inc/smtp_mail.class.php";//$smtp_server="mail.eccreation.com";
    $smtp_server="mail.handsome.com.cn";
    $from="[email protected]";
    $to="[email protected]";
    $subject="test HERE!!!!!!";
    $body="test only!
    fdsafsdajfkljdklsfsda
    fds
    fdsaf
    dsfsdafds
    f";$mail=new text_mail;
    $mail->from=$from;
    $mail->to=$to;
    $mail->subject=$subject;
    $mail->body=$body;
    $mail->reply=$from;
    $data=$mail->get_mail();
    $smtp=new smtp_mail;
    $smtp->debugMode=true;
    $smtp->authMode=true;
    $smtp->authLastLine="OK";  //ehlo命令列出的最后一行的文字
    smtp->authLoginName="test";
    $smtp->authLoginPassword="123456";
    $smtp->send_email($smtp_server,$from,$to,$data);
    ?>
      

  23.   

    测试结果:CONNECTING mail.handsome.com.cn ...Succeed!
    RESPONSE: 220 hsexchange.hs.handsome.com.cn Microsoft ESMTP MAIL Service, Version: 5.0.2195.4453 ready at Mon, 15 Jul 2002 11:20:34 +0800 
    CWD: HELO 
    RESPONSE: 250 hsexchange.hs.handsome.com.cn Hello [211.66.123.157] 
    CWD: EHLO 
    RESPONSE: 250-hsexchange.hs.handsome.com.cn Hello [211.66.123.157] 
    RESPONSE: 250-TURN 
    RESPONSE: 250-ATRN 
    RESPONSE: 250-SIZE 10240000 
    RESPONSE: 250-ETRN 
    RESPONSE: 250-PIPELINING 
    RESPONSE: 250-DSN 
    RESPONSE: 250-ENHANCEDSTATUSCODES 
    RESPONSE: 250-8bitmime 
    RESPONSE: 250-BINARYMIME 
    RESPONSE: 250-CHUNKING 
    RESPONSE: 250-VRFY 
    RESPONSE: 250-X-EXPS GSSAPI NTLM LOGIN 
    RESPONSE: 250-X-EXPS=LOGIN 
    RESPONSE: 250-AUTH GSSAPI NTLM LOGIN 
    RESPONSE: 250-AUTH=LOGIN 
    RESPONSE: 250-X-LINK2STATE 
    RESPONSE: 250-XEXCH50 
    RESPONSE: 250 OK 
    CWD: AUTH LOGIN 
    RESPONSE: 334 VXNlcm5hbWU6 
    CWD: dGVzdA== 
    RESPONSE: 334 UGFzc3dvcmQ6 
    CWD: MTIzNDU2 
    RESPONSE: 235 2.7.0 Authentication successful. 
    CWD: MAIL FROM: [email protected] 
    RESPONSE: 250 2.1.0 [email protected] OK 
    CWD: RCPT TO: [email protected] 
    RESPONSE: 250 2.1.5 [email protected] 
    CWD: DATA 
    RESPONSE: 354 Start mail input; end with . 
    CWD: . 
    RESPONSE: 250 2.6.0 Queued mail for delivery 
    CWD: QUIT 
    RESPONSE: 221 2.0.0 hsexchange.hs.handsome.com.cn Service closing transmission channel 
      

  24.   

    原因分析:
        主要的原因在于在用socket打开这些smtp,ftp的文件流,用户和server双方需要不断进行交流,这就意味着在交流过程中文件流不会出现eof,所有在程序进行协议过程中不能使用fread等靠eof判断文件结束的语句,而只能使用fgets靠判断\r\n而结束读取的函数.
        在输入ehlo命令后,服务器将返回多行的可以使用的协议列表.如:
    250-hsexchange.hs.handsome.com.cn Hello [211.66.123.157] 
    250-TURN 
    250-ATRN 
    250-SIZE 10240000 
    ........
    250-AUTH GSSAPI NTLM LOGIN 
    250-AUTH=LOGIN 
    250-X-LINK2STATE 
    250-XEXCH50 
    250 OK 
        因为这些行都会在文件流的缓冲里面,如果不把这些行全部读取完毕,那么在执行下一个命令后,就会得不到希望的返回码而只会继续读取文件流当前位置的下一行.这就是为什么执行了auth login之后会出现250-turn的原因.
        现在我把类加了一个authLastLine的属性,判断这个服务器执行ehlo之后列表的最后一行是什么,然后把所有行缓冲读取完毕,以便继续执行下面的命令.如上服务器,最后一行输出是OK,所以要把authLastLine的值赋予"OK" ($smtp->authLastLine="OK";)
        这个方法的确比较笨,因为对于不同服务器都需要先知道它执行了ehlo之后最后一行是什么(要自己telnet过去自己查出来),不够智能化.但是我也实在想不到有什么其他的函数可以用了,因为php本身的函数库就有这个限制.
      

  25.   

    看来是因为服务器的响应没有读完的缘故
    这时候发送命令,服务器可能会不认当时,虽然看到在telnet和PHP下,有细小的差别,但是没有考虑太多,是我疏忽了,,,