PHP基于SMTP协议实现邮件发送实例代码


Posted in PHP onApril 27, 2017

SMTP协议

当我们使用PHP的第三方库或工具类进行邮件发送的时候,是否想过一个问题:

为什么我们不能自己写php代码实现邮件发现,而要用别人的库呢?php发送邮件到底是如何实现的?

首先我们要了解发送邮件的基本原理,本文基于SMTP协议实现邮件发送

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议。简单来说它定义了一组规则,我们只需要依照这个规则来告诉SMTP服务器,我们要发送邮件的发送人,接收人,内容,主题等信息。

然后SMTP服务器依照这组规则来解析我们发送的信息,最后进行邮件发送。
像163,qq等邮件服务器都有提供SMTP服务,我们只要连接上他们的SMTP服务器,然后write数据,就能实现邮件发送了。

其实我们可以不写代码,直接借用Linux的telnet工具来连接smtp服务,进行邮件发送。借此来了解邮件发送的整个流程。

telnet进行邮件发送

我们可以在linux环境下,使用telnet命令,连接163的smtp服务,25端口(一般smtp都是用25端口),借此来理解smtp的传输流程。

telnet smtp.163.com 25

然后会得到以下结果,说明我们连接成功了

Trying 220.181.12.16...
Connected to smtp.163.com.
Escape character is '^]'.
220 163.com Anti-spam GT for Coremail System (163com[20141201])

接着我们执行以下命令,告诉对方我们的身份标识来自哪里

HELO smtp.163.com

对方会返回给我们一个250 OK

再执行AUTH LOGIN告诉对方我们要开始进行身份认证,然后对方会回应我们一些消息。

后面我们会再输入我们的用户名,密码,发送邮件的内容,发送人,接受人等信息,然后结束对话,smtp服务器就会帮我们把邮件发送出去。

由于smtp协议对邮件内容格式有严格的要求,在命令行中不好执行,所以这里没有将整个过程执行完毕,后面会使用php代码完整实现。

从上面使用telnet连接smtp邮件的过程可以看出来,发送邮件的过程其实很简单,就是连接smtp服务的25端口,依照协议告诉对方我们要发什么邮件即可。这与平台,与编程语言无关。

无论我们用C语言,还是Java或者PHP,只要使用Socket连接SMTP服务器,就能实现邮件发送。

SMTP指令

上面我们使用telnet连接smtp服务时,输入了一些HELO ,AUTH LOGIN等,大家可能会有疑问这些是什么。

其实很简单,这些就是SMTP协议定义的指令,或者说规则,smtp服务器就是通过这些指令才知道我们是想干啥。

常用指令如下:

指令 作用
HELO 向对方邮件服务器发出的标识自己的身份的命令
AUTH LOGIN 即将进行身份认证
MAIL FROM 告诉对方本次邮件发送人是谁
RCPT TO 发送给谁
DATA 告诉对方本次邮件,接下来我们发送邮件具体内容了
QUIT 邮件内容输入完毕后,执行该指令退出

php实现邮件发送

直接上代码

class Mailer
{
  private $host;
  private $port = 25;
  private $user;
  private $pass;
  private $debug = false;
  private $sock;

  public function __construct($host,$port,$user,$pass,$debug = false)
  {
    $this->host = $host;
    $this->port = $port;
    $this->user = base64_encode($user); //用户名密码一定要使用base64编码才行
    $this->pass = base64_encode($pass);
    $this->debug = $debug;
  //socket连接
    $this->sock = fsockopen($this->host,$this->port);
    if(!$this->sock){
      exit('出错啦');
    }
  //读取smtp服务返回给我们的数据
    $response = fgets($this->sock);
    $this->debug($response);
        //如果响应中有220返回码,说明我们连接成功了
    if(strstr($response,'220') === false){
      exit('出错啦');
    }
  }
//发送SMTP指令,不同指令的返回码可能不同
  public function execCommand($cmd,$return_code){
    fwrite($this->sock,$cmd);

    $response = fgets($this->sock);
//输出调试信息
    $this->debug('cmd:'.$cmd .';response:'.$response);
    if(strstr($response,$return_code) === false){
      return false;
    }
    return true;
  }

  public function sendMail($from,$to,$subject,$body){
//detail是邮件的内容,一定要严格按照下面的格式,这是协议规定的
    $detail = 'From:'.$from."\r\n";
    $detail .= 'To:'.$to."\r\n";
    $detail .= 'Subject:'.$subject."\r\n";
    $detail .= 'Content-Type: Text/html;'."\r\n";
    $detail .= 'charset=gb2312'."\r\n\r\n";
    $detail .= $body;
    $this->execCommand("HELO ".$this->host."\r\n",250);
    $this->execCommand("AUTH LOGIN\r\n",334);
    $this->execCommand($this->user."\r\n",334);
    $this->execCommand($this->pass."\r\n",235);
    $this->execCommand("MAIL FROM:<".$from.">\r\n",250);
    $this->execCommand("RCPT TO:<".$to.">\r\n",250);
    $this->execCommand("DATA\r\n",354);
    $this->execCommand($detail."\r\n.\r\n",250);
    $this->execCommand("QUIT\r\n",221);
  }

  public function debug($message){
    if($this->debug){
      echo '<p>Debug:'.$message . PHP_EOL .'</p>';
    }
  }

  public function __destruct()
  {
    fclose($this->sock);
  }

}

调用示例

$port = 25;
$user = 'username'; //请替换成你自己的smtp用户名
$pass = 'pass'; //请替换成你自己的smtp密码
$host = 'smtp.163.com';
$from = 'xxxxx@163.com'; 
$to = 'xxxx@qq.com';
$body = 'hello world';
$subjet = '我是标题';
$mailer = new Mailer($host,$port,$user,$pass,true);
$mailer->sendMail($from,$to,$subjet,$body);

在执行指令时有输出调试信息,输出了我们每次执行的指令以及smtp服务返回给我们的响应数据。

因此我们可以看到以下结果

Debug:220 163.com Anti-spam GT for Coremail System (163com[20141201])

Debug:cmd:HELO smtp.163.com ;response:250 OK

Debug:cmd:AUTH LOGIN ;response:334 dXNlcm5hbWU6

Debug:cmd:aXR6aG91anVuYmxvZ0AxNjMuY29t ;response:334 UGFzc3dvcmQ6

Debug:cmd:QzBjSGRRNe32xiNGFYUE5oag== ;response:235 Authentication successful

Debug:cmd:MAIL FROM: ;response:250 Mail OK

Debug:cmd:RCPT TO:<380472723@qq.com> ;response:250 Mail OK

Debug:cmd:DATA ;response:354 End data with .

Debug:cmd:From:itzhoujunblog@163.com To:380472723@qq.com Subject:我是标题 Content-Type: Text/html; charset=gb2312 hello world . ;response:250 Mail OK queued as smtp11,D8CowACXHE5APdNYCo0hAQ--.19144S2 1490238785

Debug:cmd:QUIT ;response:221 Bye

总结

邮件发送步骤

  1. 使用socket连接smtp服务
  2. 使用smtp指令进行对话,输入身份信息,邮件信息等
  3. 结束对话

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
留言板翻页的实现详解
Oct 09 PHP
Smarty模板快速入门
Jan 04 PHP
配置Apache2.2+PHP5+CakePHP1.2+MySQL5运行环境
Apr 25 PHP
PHP5.3.1 不再支持ISAPI
Jan 08 PHP
php语言流程控制中的主动与被动
Nov 05 PHP
Zend的Registry机制的使用说明
May 02 PHP
探讨PHP中this,self,parent的区别详解
Jun 08 PHP
关于PHP二进制流 逐bit的低位在前算法(详解)
Jun 13 PHP
PHP文件操作实例总结【文件上传、下载、分页】
Dec 08 PHP
PHP使用 Pear 进行安装和卸载包的方法详解
Jul 08 PHP
PHP中通过getopt解析GNU C风格命令行选项
Nov 18 PHP
php获取是星期几的的一些常用姿势
Dec 15 PHP
PHP编程实现多维数组按照某个键值排序的方法小结【2种方法】
Apr 27 #PHP
PHP使用第三方即时获取物流动态实例详解
Apr 27 #PHP
利用PHPExcel实现Excel文件的写入和读取
Apr 26 #PHP
PHP基于接口技术实现简单的多态应用完整实例
Apr 26 #PHP
PHP基于简单递归函数求一个数阶乘的方法示例
Apr 26 #PHP
php编程实现简单的网页版计算器功能示例
Apr 26 #PHP
Mac下php 5升级到php 7的步骤详解
Apr 26 #PHP
You might like
php引用计数器进行垃圾收集机制介绍
2012/09/19 PHP
ThinkPHP令牌验证实例
2014/06/18 PHP
php使用parse_url和parse_str解析URL
2015/02/22 PHP
PHP实现恶意DDOS攻击避免带宽占用问题方法
2015/05/27 PHP
PHP实现链式操作的核心思想
2015/06/23 PHP
PHP使用mongoclient简单操作mongodb数据库示例
2019/02/08 PHP
php的优点总结 php有哪些优点
2019/07/19 PHP
redis+php实现微博(一)注册与登录功能详解
2019/09/23 PHP
兼容多浏览器的iframe自适应高度(ie8 、谷歌浏览器4.0和 firefox3.5.3)
2009/11/04 Javascript
鼠标拖动实现DIV排序示例代码
2013/10/14 Javascript
js控制文本框只能输入中文、英文、数字与指定特殊符号的实现代码
2016/09/09 Javascript
jQuery.datatables.js插件用法及api实例详解
2016/10/28 Javascript
基于Bootstrap的网页设计实例
2017/03/01 Javascript
node.js中debug模块的简单介绍与使用
2017/04/25 Javascript
基于AngularJS的简单使用详解
2017/09/10 Javascript
微信小程序中使用ECharts 异步加载数据的方法
2018/06/27 Javascript
JavaScript设计模式---单例模式详解【四种基本形式】
2020/05/16 Javascript
wxPython窗口的继承机制实例分析
2014/09/28 Python
Python常用知识点汇总
2016/05/08 Python
Python Socket传输文件示例
2017/01/16 Python
启动targetcli时遇到错误解决办法
2017/10/26 Python
Windows下的Jupyter Notebook 安装与自定义启动(图文详解)
2018/02/21 Python
python 多维切片之冒号和三个点的用法介绍
2018/04/19 Python
Python安装OpenCV的示例代码
2020/03/05 Python
django 数据库返回queryset实现封装为字典
2020/05/19 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
2020/05/22 Python
详解pytorch tensor和ndarray转换相关总结
2020/09/03 Python
Django URL参数Template反向解析
2020/11/24 Python
美国男士和女士奢侈品折扣手表购物网站:Certified Watch Store
2018/06/13 全球购物
基层工作经历证明
2014/01/13 职场文书
简历自我评价模版
2014/01/31 职场文书
法学专业自我鉴定
2014/02/05 职场文书
运动会800米加油稿
2014/02/22 职场文书
大明湖导游词
2015/02/03 职场文书
2015年体育教师个人工作总结
2015/05/12 职场文书
Python中os模块的简单使用及重命名操作
2021/04/17 Python