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 相关文章推荐
php 文章采集正则代码
Dec 28 PHP
PHP 5.3.1 安装包 VC9 VC6不同版本的区别是什么
Jul 04 PHP
php addslashes 利用递归实现使用反斜线引用字符串
Aug 05 PHP
php json_encode值中大括号与花括号区别
Sep 30 PHP
浅谈json_encode用法
Mar 05 PHP
wamp服务器访问php非常缓慢的解决过程
Jul 01 PHP
php基于PDO实现功能强大的MYSQL封装类实例
Feb 27 PHP
PHP 实现人民币小写转换成大写的方法及大小写转换函数
Nov 17 PHP
yii2 url重写并隐藏index.php方法
Dec 10 PHP
PHP实现无限极分类的两种方式示例【递归和引用方式】
Mar 25 PHP
使用Entrust扩展包在laravel 中实现RBAC的功能
Mar 16 PHP
PHP终止脚本运行三种实现方法详解
Sep 01 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
DC漫画《蝙蝠侠和猫女》图透 猫女怀孕老爷当爹
2020/04/09 欧美动漫
php异步多线程swoole用法实例
2014/11/14 PHP
使用URL传输SESSION信息
2015/07/14 PHP
PHP微信刮刮卡 附微信接口
2016/07/22 PHP
PHP实现的redis主从数据库状态检测功能示例
2017/07/20 PHP
php和C#的yield迭代器实现方法对比分析
2019/07/17 PHP
PHP迭代器和生成器用法实例分析
2019/09/28 PHP
js写一个弹出层并锁屏效果实现代码
2012/12/07 Javascript
js实现iframe跨页面调用函数的方法
2014/12/13 Javascript
js给selected添加options的方法
2015/05/06 Javascript
jquery带翻页动画的电子杂志代码分享
2015/08/21 Javascript
jQuery遍历节点树方法分析
2016/09/08 Javascript
AngularJS实现网站换肤实例
2021/02/19 Javascript
Bootstrap jquery.twbsPagination.js动态页码分页实例代码
2017/02/20 Javascript
vue 实现全选全不选的示例代码
2018/03/29 Javascript
微信小程序调用微信支付接口的实现方法
2019/04/29 Javascript
AI小程序之语音听写来了,十分钟掌握百度大脑语音听写全攻略
2020/03/13 Javascript
微信小程序学习总结(五)常见问题实例小结
2020/06/04 Javascript
原生JavaScript实现随机点名表
2021/01/14 Javascript
Python三元运算实现方法
2015/01/12 Python
python实现查找excel里某一列重复数据并且剔除后打印的方法
2015/05/26 Python
总结用Pdb库调试Python的方式及常用的命令
2016/08/18 Python
Python正则表达式如何进行字符串替换实例
2016/12/28 Python
python操作oracle的完整教程分享
2018/01/30 Python
python print出共轭复数的方法详解
2019/06/25 Python
Python Celery多队列配置代码实例
2019/11/22 Python
Django使用Celery加redis执行异步任务的实例内容
2020/02/20 Python
python读取多层嵌套文件夹中的文件实例
2020/02/27 Python
Selenium基于PIL实现拼接滚动截图
2020/04/10 Python
Python爬虫爬取有道实现翻译功能
2020/11/27 Python
美国知名的网上鞋类及相关服装零售商:Shoes.com
2017/05/06 全球购物
后勤工作职责
2013/12/22 职场文书
民族学专业职业生涯规划范文:积跬步以至千里
2014/09/11 职场文书
乡镇干部党的群众路线教育实践活动个人对照检查材料
2014/09/24 职场文书
运动会通讯稿600字
2015/07/20 职场文书
2019送给家人们的中秋节祝福语
2019/08/15 职场文书