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去掉字符串的最后一个字符附substr()的用法
Mar 23 PHP
php入门学习知识点六 PHP文件的读写操作代码
Jul 14 PHP
PHP获取http请求的头信息实现步骤
Dec 16 PHP
谈谈关于php的优点与缺点
Apr 11 PHP
php实现水仙花数的4个示例分享
Apr 08 PHP
PHP中实现crontab代码分享
Mar 26 PHP
PHP会话处理的10个函数
Aug 11 PHP
PHP安全下载文件的方法
Apr 07 PHP
php 访问oracle 存储过程实例详解
Jan 08 PHP
浅析PHP类的反射来实现依赖注入过程
Feb 06 PHP
PHP使用PDO抽象层获取查询结果的方法示例
May 10 PHP
PHP论坛实现积分系统的思路代码详解
Jun 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
php去除重复字的实现代码
2011/09/16 PHP
WordPress中用于获取及自定义头像图片的PHP脚本详解
2015/12/17 PHP
Yii2中使用asset压缩js,css文件的方法
2016/11/24 PHP
PHP Redis扩展无法加载的问题解决方法
2019/08/22 PHP
Laravel + Elasticsearch 实现中文搜索的方法
2020/02/02 PHP
最佳JS代码编写的14条技巧
2011/01/09 Javascript
Uglifyjs(JS代码优化工具)入门 安装使用
2020/04/13 Javascript
IE下使用jQuery重置iframe地址时内存泄露问题解决办法
2015/02/05 Javascript
JS实现图片产生波纹一样flash效果的方法
2015/02/27 Javascript
node.js 和HTML5开发本地桌面应用程序
2016/12/13 Javascript
JS正则替换去空格的方法
2017/03/24 Javascript
JavaScript封闭函数及常用内置对象示例
2019/05/13 Javascript
在vue中根据光标的显示与消失实现下拉列表
2019/09/29 Javascript
Vue+abp微信扫码登录的实现代码示例
2020/01/06 Javascript
微信小程序报错: thirdScriptError的错误问题
2020/06/19 Javascript
python解析xml文件操作实例
2014/10/05 Python
Python排序算法之选择排序定义与用法示例
2018/04/29 Python
Django项目中用JS实现加载子页面并传值的方法
2018/05/28 Python
使用python 写一个静态服务(实战)
2019/06/28 Python
python字典嵌套字典的情况下找到某个key的value详解
2019/07/10 Python
Python3.8对可迭代解包的改进及用法详解
2019/10/15 Python
Python3中configparser模块读写ini文件并解析配置的用法详解
2020/02/18 Python
Python Django中的STATIC_URL 设置和使用方式
2020/03/27 Python
套娃式文件夹如何通过Python批量处理
2020/08/23 Python
Europcar英国:英国汽车和货车租赁
2017/01/21 全球购物
Cotton On南非:澳洲时尚平价品牌
2018/06/28 全球购物
如何进行Linux分区优化
2013/02/12 面试题
好矿嫂事迹材料
2014/01/21 职场文书
留学推荐信怎么写
2014/01/25 职场文书
《我为你骄傲》教学反思
2014/02/20 职场文书
让世界充满爱观后感
2015/06/10 职场文书
会计专业自荐信范文
2019/05/22 职场文书
导游词之丽江普济寺
2019/10/22 职场文书
PyQt5爬取12306车票信息程序的实现
2021/05/14 Python
Python中使用ipython的详细教程
2021/06/22 Python
Windows10安装Apache2.4的方法步骤
2022/06/25 Servers