php 邮件发送是html 没样式_PHP 下的 Socket 编程--发送邮件

发送邮件使用的是 SMTP 协议 (简单邮件传输协议), 用于邮件服务器和邮件发送方之间.

邮件的发送过程大致如下:

  1. 在邮件发送方和邮件服务器间建立 TCP 连接, 服务器响应 220 表示连接成功;
  2. 发送方通过HELO命令标识自己的身份. 服务器响应 250 表示准备接收邮件;
  3. 发送方通过AUTH LOGIN命令进行登录, 以 163 邮件服务器为例, 登录账号分别是 base64 编码过的邮箱账号和 163 的客户端授权码. 服务器响应 334 表示账号验证通过, 响应 235 表示授权码验证通过;
  4. 发送方通过MAIL FROM命令指定邮件的发送者. 服务器响应 250 表示成功;
  5. 发送方通过RCPT TO命令指定邮件接收地址, 服务器响应 250 表示成功;
  6. 发送方通过DATA命令发送邮件, 邮件内容包括邮件头和邮件正文部分. 服务器响应 250 表示成功;
  7. 发送方通过QUIT命令断开连接.

Windows 下可以通过 telnet 发送邮件.

邮件头的基本格式为:

Date: Feb 7 20:30:39 2007 // 发送日期
From: "发送者" <发送者邮箱>
To: "接受者" <接收者邮箱>
Subject: 邮件标题
Content-Type: text/plain; // 邮件正文类型

邮件头主要配置项:

7d44ff579d9aa5578c5892064a6f5f76.png

邮件内容的具体格式和结构, 可以参考: https://help.aliyun.com/knowledge_detail/51584.html

示例一: 发送简单邮件

sendEmail.php

header('Content-type: text/html; charset=utf-8');
$server = 'smtp.163.com';
$errno = null;
$error = null;
// 设置邮件头
$email = 'Date: ' . date('j F G:i:s Y', time()) . "rn"; // 发送日期
$email .= 'From: "哪里多" <******@163.com>' . "rn"; // 发送者
$email .= "To: "二柱子" <******@qq.com>rn"; // 接收者
$email .= "Subject: 测试邮件rn"; // 邮件标题
$email .= "Content-Type: text/plain; charset=UTF-8rnrn"; // 邮件内容类型,邮件头和正文间空一行分割
 
$email .= "邮件正文rnrnrn"; // 正文内容结束后空两行
 
 
$email .= ".rn"; // 邮件内容结束后以 . 命令标识结束
try
{
// fsockopen()函数: 建立一个网络连接或Unix套接字, 返回一个文件句柄, 可以使用文件操作函数操作返回的资源
$sockHandle = fsockopen($server, 25, $errno, $error, 60);
if($sockHandle === false)
{
exit('无法建立连接');
}
$response = fgets($sockHandle);
if(strpos($response, '220') !== 0)
{
exit('连接邮件服务器失败');
}
fwrite($sockHandle, "HELO test-userrn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('helo命令执行失败');
}
fwrite($sockHandle, "AUTH LOGINrn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('AUTH LOGIN命令执行失败');
}
fwrite($sockHandle, base64_encode("******@163.com") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('账号验证失败');
}
// 163邮箱的客户端授权码
fwrite($sockHandle, base64_encode("******") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '235') !== 0)
{
exit('密码验证失败');
}
fwrite($sockHandle, "MAIL FROM: <******@163.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('mail from命令执行失败');
}
fwrite($sockHandle, "RCPT TO: <******@qq.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('rcpt to命令执行失败');
}
fwrite($sockHandle, "DATArn");
$response = fgets($sockHandle);
if(strpos($response, '354') !== 0)
{
exit('data命令执行失败');
}
fwrite($sockHandle, $email);
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('发送邮件失败');
}
fwrite($sockHandle, "QUITrn");
echo '发送邮件成功' . PHP_EOL;
fclose($sockHandle);
}catch(Exception $e)
{
var_dump($e->getMessage());
var_dump($e->getTrace());
var_dump($e->getLine());
fclose($sockHandle);
}

通过命令行执行脚本文件:

00d76bb330fc5917374c7f042b0d97d3.png

a7ae9abdbd0f670e6f981442f5d59032.png

示例二: 发送携带单个附件的邮件

邮件携带附件时, 邮件头的格式类似 HTTP 请求中的上传文件时请求头的格式, 都需要在头部附加说明附件的内容和其他信息.

sendEmailWithAttachment.php

header('Content-type: text/html; charset=utf-8');
$server = 'smtp.163.com';
$errno = null;
$error = null;
// 设置邮件头
$email = 'Date: ' . date('j F G:i:s Y', time()) . "rn";
$email .= 'From: "哪里多" <***@163.com>' . "rn";
$email .= "To: "二柱子" <***@qq.com>rn";
$email .= "Subject: 测试邮件rn";
$email .= "Content-Type: multipart/mixed; boundary=----delimiter1----rnrn"; // 与post方式上传附件类似,需设定段体边界。邮件头和正文间空一行分割
 
$email .= "------delimiter1----rn"; // 段体开始边界格式:--{$boundary}
$email .= "Content-Type: multipart/alternative; boundary=----delimiter2----rnrn"; // 邮件正文段体边界
 
$email .= "------delimiter2----rn"; // 邮件正文部分段体
$email .= "Content-Type: text/plain; charset=UTF-8rnrn";
 
$email .= "邮件正文rn"; // 正文内容结束后空两行
$email .= "------delimiter2------rnrn"; // 正文部分结束边界,段体结束边界格式:--{$boundary}--
 
$email .= "------delimiter1----rn"; // 邮件附件段体设置,发送多个附件时,重复设置该部分段体
$email .= "Content-Type: application/octet-stream; name="1542533630(1).jpg"rn";
$email .= "Content-Transfer-Encoding: base64rn";
$email .= "Content-Disposition: attachment; filename="test.jpg"rnrn"; // 附件下载名称
 
$file = base64_encode(file_get_contents('./1542533630(1).jpg'));
$email .= $file . "rnrn";
 
$email .= "------delimiter1------rn"; // 邮件实体设置结束边界
$email .= ".rn"; // 邮件内容结束后以 . 命令表示邮件内容设置完成
try
{
// fsockopen()函数: 建立一个网络连接或Unix套接字, 返回一个文件句柄, 可以使用文件操作函数操作返回的资源
$sockHandle = fsockopen($server, 25, $errno, $error, 60);
if($sockHandle === false)
{
exit('无法建立连接');
}
$response = fgets($sockHandle);
if(strpos($response, '220') !== 0)
{
exit('连接邮件服务器失败');
}
fwrite($sockHandle, "HELO test-userrn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('helo命令执行失败');
}
fwrite($sockHandle, "AUTH LOGINrn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('AUTH LOGIN命令执行失败');
}
fwrite($sockHandle, base64_encode("***@163.com") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('账号验证失败');
}
// 163邮箱的客户端授权码
fwrite($sockHandle, base64_encode("***") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '235') !== 0)
{
exit('密码验证失败');
}
fwrite($sockHandle, "MAIL FROM: <***@163.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('mail from命令执行失败');
}
fwrite($sockHandle, "RCPT TO: <***@qq.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('rcpt to命令执行失败');
}
fwrite($sockHandle, "DATArn");
$response = fgets($sockHandle);
if(strpos($response, '354') !== 0)
{
exit('data命令执行失败');
}
fwrite($sockHandle, $email);
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('发送邮件失败');
}
fwrite($sockHandle, "QUITrn");
echo '发送邮件成功' . PHP_EOL;
fclose($sockHandle);
}catch(Exception $e)
{
var_dump($e->getMessage());
var_dump($e->getTrace());
var_dump($e->getLine());
fclose($sockHandle);
}

通过命令行运行脚本:

6a3724a3165808bb1bdedaa96ddfdd4c.png

邮箱成功接收到邮件:

8a3effab29b8cbae286354d363eced9e.png

示例三: 发送携带多个附件的邮件

sendEmailWithMultiAttachment.php

header('Content-type: text/html; charset=utf-8');
$server = 'smtp.163.com';
$errno = null;
$error = null;
// 设置邮件头
$email = 'Date: ' . date('j F G:i:s Y', time()) . "rn";
$email .= 'From: "哪里多" <***@163.com>' . "rn";
$email .= "To: "二柱子" <***@qq.com>rn";
$email .= "Subject: 急报rn";
$email .= "Content-Type: multipart/mixed; boundary=----delimiter1----rnrn"; // 邮件头和正文间空一行分割
 
$email .= "------delimiter1----rn";
$email .= "Content-Type: multipart/alternative; boundary=----delimiter2----rnrn";
 
$email .= "------delimiter2----rn";
$email .= "Content-Type: text/plain; charset=UTF-8rnrn";
 
$email .= "见信速回rn"; // 正文内容结束后空两行
$email .= "------delimiter2------rnrn";
 
$email .= "------delimiter1----rn"; // 附件1段体
$email .= "Content-Type: application/octet-stream; name="baobiao1.jpg"rn";
$email .= "Content-Transfer-Encoding: base64rn";
$email .= "Content-Disposition: attachment; filename="baobiao1.jpg"rnrn"; // 附件下载名称
 
$file = base64_encode(file_get_contents('./1542533630(1).jpg'));
$email .= $file . "rnrn";
 
$email .= "------delimiter1----rn"; // 附件2段体
$email .= "Content-Type: application/octet-stream; name="baobiao2.jpg"rn";
$email .= "Content-Transfer-Encoding: base64rn";
$email .= "Content-Disposition: attachment; filename="baobiao2.jpg"rnrn"; // 附件下载名称
 
//$file = base64_encode(file_get_contents('./1542533630(1).jpg'));
$email .= $file . "rnrn";
 
$email .= "------delimiter1------rn";
$email .= ".rn"; // 邮件内容结束后以 . 命令标识结束
try
{
// fsockopen()函数: 建立一个网络连接或Unix套接字, 返回一个文件句柄, 可以使用文件操作函数操作返回的资源
$sockHandle = fsockopen($server, 25, $errno, $error, 60);
if($sockHandle === false)
{
exit('无法建立连接');
}
$response = fgets($sockHandle);
if(strpos($response, '220') !== 0)
{
exit('连接邮件服务器失败');
}
fwrite($sockHandle, "HELO test-userrn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('helo命令执行失败');
}
fwrite($sockHandle, "AUTH LOGINrn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('AUTH LOGIN命令执行失败');
}
fwrite($sockHandle, base64_encode("***@163.com") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '334') !== 0)
{
exit('账号验证失败');
}
// 163邮箱的客户端授权码
fwrite($sockHandle, base64_encode("***") . "rn");
$response = fgets($sockHandle);
if(strpos($response, '235') !== 0)
{
exit('密码验证失败');
}
fwrite($sockHandle, "MAIL FROM: <***@163.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('mail from命令执行失败');
}
fwrite($sockHandle, "RCPT TO: <***@qq.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
exit('rcpt to命令执行失败');
}
fwrite($sockHandle, "DATArn");
$response = fgets($sockHandle);
if(strpos($response, '354') !== 0)
{
exit('data命令执行失败');
}
fwrite($sockHandle, $email);
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
echo $response;
exit('发送邮件失败');
}
fwrite($sockHandle, "QUITrn");
echo '发送邮件成功' . PHP_EOL;
fclose($sockHandle);
}catch(Exception $e)
{
var_dump($e->getMessage());
var_dump($e->getTrace());
var_dump($e->getLine());
fclose($sockHandle);
}

通过命令行运行脚本:

5f615d3db9f978e37f120d0580d0c826.png

邮箱接收到邮件:

307708a8ffa81f50340b2a95957a8289.png

如果需要设置抄送项, 在邮件头中配置抄送项Cc即可, 如:

Cc: <抄送人1@qq.com>, <抄送人2@163.com>

然后通过执行命令RCPT TO设置抄送人:

fwrite($sockHandle, "RCPT TO: <抄送人2@163.com>rn");
$response = fgets($sockHandle);
if(strpos($response, '250') !== 0)
{
echo $response;
exit('抄送命令执行失败');
}

退信的处理

邮件内容不规范, 或相同内容重复发送时, 可能导致退信, 发送失败.

  1. 如果是重复内容反复发送导致的退信, 更换发送人账号即可.
  2. 也可以通过将发件人添加到收件人解决退信问题. 此时邮件头中To的配置项为:
To: 收件人1 <***@qq.com>, 发件人 <***@163.com>rn

 在通过命令设置发件人时, 通过反复执行RCPT TO命令, 设置多个收件人.

fwrite($sockHandle, "RCPT TO:<收件人1@qq.com>rn");
    $response = fgets($sockHandle);
    if(strpos($response, '250') !== 0)
    {
    exit('收件人1命令执行失败');
    }
    fwrite($sockHandle, "RCPT TO: <发件人@163.com>rn");
    $response = fgets($sockHandle);
    if(strpos($response, '250') !== 0)
    {
    exit('收件人2命令执行失败');
    }

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要

PHP进阶架构师>>>视频、面试文档免费获取​shimo.im
87a431ba28852d3a9e0bb532bdb995bd.png

或 者关注咱们下面的知乎专栏

PHP架构师圈子​zhuanlan.zhihu.com
ed4433f4624649e904ec9b886b37a38b.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值