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可能遇到的问题“无法载入mysql扩展” 的解决方法
Apr 16 PHP
php一些错误处理的方法与技巧总结
Aug 10 PHP
php生成图形(Libchart)实例
Nov 06 PHP
php实现把数组按指定的个数分隔
Feb 17 PHP
PHP实现的MongoDB数据库操作类分享
May 12 PHP
php中使用session防止用户非法登录后台的方法
Jan 27 PHP
php多重接口的实现方法
Jun 20 PHP
PHP处理会话函数大总结
Aug 05 PHP
php生成mysql的数据字典
Jul 07 PHP
PHP简单实现DES加密解密的方法
Jul 12 PHP
ThinkPHP实现简单登陆功能
Apr 28 PHP
PHP实现将优酷土豆腾讯视频html地址转换成flash swf地址的方法
Aug 04 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 AJAX实例根据邮编自动完成地址信息
2008/11/23 PHP
PHP中调用ASP.NET的WebService的代码
2011/04/22 PHP
file_get_contents获取不到网页内容的解决方法
2013/03/07 PHP
解析smarty 截取字符串函数 truncate的用法介绍
2013/06/20 PHP
PHP实现动态创建XML文档的方法
2018/03/30 PHP
自己用jQuery写了一个图片的马赛克消失效果
2014/05/04 Javascript
jQuery检测滚动条是否到达底部
2015/12/15 Javascript
angular bootstrap timepicker TypeError提示怎么办
2017/06/13 Javascript
NodeJS实现图片上传代码(Express)
2017/06/30 NodeJs
js实现左右两侧浮动广告
2018/07/09 Javascript
vue component 中引入less文件报错 Module build failed
2019/04/17 Javascript
如何使用JavaScript实现栈与队列
2019/06/24 Javascript
vue.js中使用微信扫一扫解决invalid signature问题(完美解决)
2020/04/11 Javascript
[01:30:54]《加油DOTA》 第三期
2014/08/18 DOTA
[40:31]Secret vs Alliacne 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
[03:00]DOTA2-DPC中国联赛1月18日Recap集锦
2021/03/11 DOTA
Python使用PIL库实现验证码图片的方法
2016/03/11 Python
详解用Python实现自动化监控远程服务器
2019/05/18 Python
Python中asyncio模块的深入讲解
2019/06/10 Python
python NumPy ndarray二维数组 按照行列求平均实例
2019/11/26 Python
python爬虫使用requests发送post请求示例详解
2020/08/05 Python
python实现启动一个外部程序,并且不阻塞当前进程
2020/12/05 Python
纯CSS3实现漂亮的input输入框动画样式库(Text input love)
2018/12/29 HTML / CSS
HTML5 transform三维立方体实现360无死角三维旋转效果
2014/08/22 HTML / CSS
关于HTML5语义标签的实践(blog页面)
2016/07/12 HTML / CSS
迪士尼法国在线商店:shopDisney FR
2020/12/03 全球购物
用Java语言将一个键盘输入的数字转化成中文输出
2013/01/25 面试题
初中地理教学反思
2014/01/11 职场文书
年会搞笑主持词串词
2014/03/24 职场文书
医院搬迁方案
2014/06/14 职场文书
12.4法制宣传日标语
2014/10/08 职场文书
汽车转让协议书
2015/01/29 职场文书
2015年创先争优工作总结
2015/05/23 职场文书
陈斌强事迹观后感
2015/06/17 职场文书
《风娃娃》教学反思
2016/02/18 职场文书
python 实现两个变量值进行交换的n种操作
2021/06/02 Python