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 相关文章推荐
第八节 访问方式 [8]
Oct 09 PHP
PHP获取类中常量,属性,及方法列表的方法
Apr 09 PHP
php提交表单时判断 if($_POST[submit])与 if(isset($_POST[submit])) 的区别
Feb 08 PHP
php中使用preg_match_all匹配文章中的图片
Feb 06 PHP
php中\r \r\n \t的区别示例介绍
Feb 08 PHP
php求两个目录的相对路径示例(php获取相对路径)
Mar 27 PHP
学习php开源项目的源码指南
Dec 21 PHP
使用Huagepage和PGO来提升PHP7的执行性能
Nov 30 PHP
php版微信公众平台入门教程之开发者认证的方法
Sep 26 PHP
利用php操作memcache缓存的基础方法示例
Aug 02 PHP
php使用curl下载指定大小的文件实例代码
Sep 30 PHP
PHP抽象类基本用法示例
Dec 28 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函数的实现原理及性能分析(三)
2015/05/13 PHP
fsockopen pfsockopen函数被禁用,SMTP发送邮件不正常的解决方法
2015/09/20 PHP
php 实现进制相互转换
2016/04/07 PHP
PHP操作MySQL中BLOB字段的方法示例【存储文本与图片】
2017/09/15 PHP
JavaScript CSS 修改学习第四章 透明度设置
2010/02/19 Javascript
JS实现在Repeater控件中创建可隐藏区域的代码
2010/09/16 Javascript
解决火狐浏览器下JS setTimeout函数不兼容失效不执行的方法
2012/11/14 Javascript
移动手机APP手指滑动切换图片特效附源码下载
2015/11/30 Javascript
深入理解JS正则表达式---分组
2016/07/18 Javascript
轻松实现js选项卡切换效果
2016/09/24 Javascript
Bootstrap基本插件学习笔记之折叠(22)
2016/12/08 Javascript
JS匹配日期和时间的正则表达式示例
2017/05/12 Javascript
小程序自定义组件实现城市选择功能
2018/07/18 Javascript
JavaScript 点击触发复制功能实例详解
2018/11/02 Javascript
详解Vue.js 作用域、slot用法(单个slot、具名slot)
2019/10/15 Javascript
进一步探究Python的装饰器的运用
2015/05/05 Python
python try except 捕获所有异常的实例
2018/10/18 Python
python读取tif图片时保留其16bit的编码格式实例
2020/01/13 Python
python目标检测给图画框,bbox画到图上并保存案例
2020/03/10 Python
解决Keras自带数据集与预训练model下载太慢问题
2020/06/12 Python
openCV提取图像中的矩形区域
2020/07/21 Python
如何把python项目部署到linux服务器
2020/08/26 Python
python语言实现贪吃蛇游戏
2020/11/13 Python
用ldap作为django后端用户登录验证的实现
2020/12/07 Python
python UDF 实现对csv批量md5加密操作
2021/01/01 Python
Grid 宫格常用布局的实现
2020/01/10 HTML / CSS
html5 canvas合成海报所遇问题及解决方案总结
2017/08/03 HTML / CSS
trivago美国:全球最大的酒店价格比较网站
2018/01/18 全球购物
亿企通软件测试面试题
2012/04/10 面试题
运动会800米加油稿
2014/02/22 职场文书
大学活动总结格式
2014/04/29 职场文书
汽车销售经理岗位职责
2014/06/09 职场文书
公司优秀员工推荐信
2015/03/24 职场文书
2016年学校爱国卫生月活动总结
2016/04/06 职场文书
九大龙王魂骨,山龙王留下躯干骨,榜首死的最憋屈(被捏碎)
2022/03/18 国漫
MySql如何将查询的出来的字段进行转换
2022/06/14 MySQL