从零构建SMTP邮件发送类

从零构建SMTP邮件发送类

SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)是由原地址到目的地址传送邮件的一组规则,用来控制信件的中转方式。SMTP协议属于TCP/IP协议簇,其使每台计算机在发送或中转信件时能到找下一个目的地。通过使用指定的服务器,把E-mail寄到收件人的服务器上。

SMTP连接和发送过程如下:

  1. 建立TCP连接。
  2. 客户端发送HELO命令标识发件人自己的身份,客户端发送MAIL命令。服务器以OK作为响应,表明准备接收。
  3. 使用AUTH命令登录SMTP服务器,输入用户名和密码(注意,用户名和密码都需要使用base64加密)。
  4. 客户端发送RCPT命令,标识该电子邮件的计划接收人,可以有多个RCPT行。服务器以OK作为想要,表示愿意为收件人发送邮件。
  5. 协商结束后,使用DATA命令发送。
  6. .号表示结束,输入内容一起发送出去,结束此次发送,用QUIT命令退出。

例如:使用Telnet创建一个SMTP会话,其中S代表服务器,C代表客户端,如下图所示

SMTP连接和发送过程

注意: 上述命令并不一定会一次性成功,服务器可能会返回错误响应,客户端应该安装协议规定的顺序输入后续命令(或重复执行失败的命令、或重置会话、或退出会话等)。

SMPT协议常用命令

SMTP协议常用命令

下面开始我们的整体,构建一个简单的SMTP邮件发送类

简单介绍一下我们使用的socket函数,我们使用fsockopen()来连接SMTP服务器,使用fsockopen的好处是把Socket连接绑定到一个流觞,然后使用各种操作流的函数操作这个Socket连接。fsockopen()函数的用法:

resource fscokopen(string $hostname, int $port, int [$errno], string [$errstr], int [$timeout]);

参数说明如下:

  • $hostname 要连接的服务器路径
  • $port 要绑定的端口
  • $errno 保存连接发生错误时的错误号
  • $errstr 保存错误信息
  • $timeout 设置连接的超时时间,单位秒

下面附上源码、注释。

<?php
namespace smtp; // 设置这个命令空间是保证不和其他冲突,可以删除
class Smtp
{
    /**
     * @var string 保存要连接的SMTP服务器
     */
    private $host;
    /**
     * @var int 要绑定的端口号
     */
    private $port = 25;
    /**
     * @var string 保存要连接的SMTP服务器用户名
     */
    private $user;
    /**
     * @var string 保存要连接的SMTP服务器密码
     */
    private $pass;
    /**
     * @var bool 标识是否开启调试模式,默认关闭
     */
    private $debug = false;
    /**
     * @var resource 保存与SMTP服务器连接的句柄
     */
    private $sock;
    /**
     * @var bool 标识使用什么格式发送邮件,false 普通文本, true HTML
     */
    private $isHTML = false;
    /**
     * @var int 连接超时时间
     */
    private $timeout = 10;

    /**
     * Smtp constructor.
     * @param string $host
     * @param int    $port
     * @param string $user
     * @param string $pass
     * @param bool   $isHTML
     * @param bool   $debug
     * @param int    $timeout
     */
    public function __construct( $host, $port, $user, $pass, $isHTML = false, $debug = false, $timeout = 10 )
    {
        $this->host   = $host;
        $this->port   = $port;
        $this->user   = base64_encode( $user );
        $this->pass   = base64_encode( $pass );
        $this->isHTML = $isHTML;
        $this->debug  = $debug;
        // 连接到服务器
        $this->sock = @fsockopen( $this->host, $this->port, $errno, $errstr, $this->timeout );
        // 判断连接是否成功
        if ( ! $this->sock ) {
            die( "Error number: $errno, Error message: $errstr" );
        }
        // 判断连接成功返回的状态码是否正确
        $response = fgets( $this->sock );
        if ( strpos( $response, "220" ) === false ) {
            die( "Server error: $response" );
        }
    }

    /**
     * 向服务器发送命令
     * @param string $cmd        需要执行的命令
     * @param int    $returnCode 命令的返回码
     * @return bool
     */
    private function doCommand( $cmd, $returnCode )
    {
        fwrite( $this->sock, $cmd );
        $response = fgets( $this->sock );
        if ( strpos( $response, "{$returnCode}" ) === false ) {
            $this->showDebug( $response );

            return false;
        }

        return true;
    }

    /**
     * 显示调试信息
     * @param $message string
     */
    private function showDebug( $message )
    {
        if ( $this->debug ) {
            echo "<p>Debug: {$message}</p>";
        }
    }

    /**
     * 判断邮件是否符合要求
     * @param $email string
     * @return bool
     */
    private function isEmail( $email )
    {
        $pattern = "/^[^_][\w]*@[\w.]+[\w]*[^_]$/";
        if ( preg_match( $pattern, $email ) ) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 执行邮件发送
     * @param string $from    发送邮件者
     * @param string $to      接收邮件者
     * @param string $subject 邮件的主题
     * @param string $body    邮件的内容
     * @return bool
     */
    public function send( $from, $to, $subject, $body )
    {
        if ( ! $this->isEmail( $from ) OR ! $this->isEmail( $to ) ) {
            $this->showDebug( "Please enter valid from/to email." );

            return false;
        }
        if ( empty( $subject ) OR empty( $body ) ) {
            $this->showDebug( "Please enter subject/content." );

            return false;
        }
        $detail = "FROM:{$from}\r\n";
        $detail .= "TO:{$to}\r\n";
        $detail .= "Subject:{$subject}\r\n";
        if ( $this->isHTML ) {
            $detail .= "Content-Type: text/html;\r\n";
        } else {
            $detail .= "Content-Type: text/plain\r\n";
        }
        $detail .= "charset=utf-8\r\n\r\n";
        $detail .= $body;
        // 执行命令
        $this->doCommand( "HELO {$this->host}\r\n", 250 ); // 客户端向服务端发送HELO表明身份
        $this->doCommand( "AUTH LOGIN\r\n", 334 ); // 客户端发送命令登录SMTP服务器
        $this->doCommand( "{$this->user}\r\n", 334 ); // 输入用户的名
        $this->doCommand( "{$this->pass}\r\n", 235 ); // 输入密码
        $this->doCommand( "MAIL FROM: <{$from}>\r\n", 250 ); // 设置发件人
        $this->doCommand( "RCPT TO: <{$to}>\r\n", 250 ); // 设置收件人
        $this->doCommand( "DATA\r\n", 354 ); // 设置邮件内容
        $this->doCommand( "$detail\r\n.\r\n", 250 ); // 发送邮件
        $this->doCommand( "QUIT\r\n", 221 ); // 退出SMTP服务器

        return true;
    }
}

// 下面是测试用例
$smtp    = new \smtp\Smtp( 'smtp.163.com', 25, 'fsyzxz@163.com', 'xfj529js515', true, true, 3 );
$subject = 'this is email subject';
$body    = 'this is test smtp email content';
var_dump( $smtp->send( 'fsyzxz@163.com', '1207791534@qq.com', $subject, $body ) );

测试结果如图:

测试结果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
//初始化邮件 void InitEmail(C01waSendMail* m_globalEmail,String sendermail,String sender,String senderpwd,String srv,int port = 25,bool bSsl = false) { m_globalEmail->m_Auth = CPJNSMTPConnection::AuthenticationMethod::AUTH_AUTO; m_globalEmail->m_bAutoDial = true; m_globalEmail->m_bSSL = bSsl; m_globalEmail->m_sUsername = sender.c_str(); m_globalEmail->m_sAddress = sendermail.c_str(); m_globalEmail->m_sPassword = senderpwd.c_str(); m_globalEmail->m_sHost = srv.c_str(); m_globalEmail->m_nPort = port; m_globalEmail->m_sEncodingFriendly = _T("Chinese Simplified (GB2312)"); m_globalEmail->m_sEncodingCharset = _T("gb2312"); m_globalEmail->m_bMime = 1; m_globalEmail->m_bHTML = 1; m_globalEmail->m_Priority = CPJNSMTPMessage::PRIORITY::NormalPriority; m_globalEmail->m_bDSN = FALSE; m_globalEmail->m_bDSNSuccess = FALSE; m_globalEmail->m_bDSNFailure = FALSE; m_globalEmail->m_bDSNDelay = FALSE; m_globalEmail->m_bDSNHeaders = FALSE; m_globalEmail->m_bDNSLookup = FALSE; } //发送邮件 void SendEmail(const char* recvemail,const char* subject,const char* body,const char * sCC=NULL,const char * bCC = NULL,const char * attach = NULL) { //初始化邮箱 //注意释放内存 C01waSendMail *m_globalEmail = new C01waSendMail; if(strlen(m_con1.m_eamil)>0&&strlen(m_con1.m_pwd)&&strlen(m_con1.m_smtpsrv)&&m_con1.port>0) { char * p = strdup(m_con1.m_eamil); char * pp = strstr(p,"@"); if(pp>0) pp[0] = '\0'; InitEmail(m_globalEmail,m_con1.m_eamil,p,m_con1.m_pwd,m_con1.m_smtpsrv,m_con1.port,m_con1.ssl); free(p); if(subject>0) m_globalEmail->m_sSubject.Format("%s",subject); if(sCC>0) m_globalEmail->m_sCC.Format("%s",sCC); if(bCC>0) m_globalEmail->m_sBCC.Format("%s",bCC); if(body>0) m_globalEmail->m_sBody.Format("%s",body); if(attach>0) m_globalEmail->m_sFile.Format("%s",attach); if(recvemail>0) m_globalEmail->m_sTo.Format("%s",recvemail); m_globalEmail->SendEmail(); } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇洒哥GG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值