在PHP中用Socket发送电子邮件

 更新时间:2016年11月25日 17:02  点击:1688

  在作者所申请的几个PHP 主页空间中,能够提供mail功能的实在不多,总是调用完mail()函数之后就毫无下文了。但是电子邮件在网上生活中的作用越来越大。想一想网虫上网不收邮件能叫真正的网虫吗?邮件的作用我不想再说了,但是假如主页空间不支持mail()发送那么怎么办呢?我也想过通过socket来实现邮件发送,但无奈对用php 进行socket编程不熟悉,再加上发送邮件要用到SMTP协议,又要读不少的英文了,所以一直也没有去研究过。终于有一天我发现了一篇文章,关于用socket编程发送邮件。我如获至宝般将其拷贝下来,并且将其改造成了一个php 可用的类,供大家使用。原来的文章只是一个简单的例子,而且还有一些错误,在我经过多次的实验、改造终于将其改成了一个直接使用socket,向指定的邮箱发送邮件的类,假如大家和前面关于发送MIME的文章结合起来,就可以实现在不支持mail()函数的网站上发送邮件了。因为发送邮件的过程需要时间,可能与mail()的处理机制还不完全一样,所以速度要慢一些,但是可以解决需要发送邮件功能的燃眉之急,同时你也可以学习用php 进行socket编程。下面就将这个类的实现原理介绍给大家,同时向大家讲解一些关于SMTP的基本知识。
Socket编程介绍

  向大家申明,本人不是一个TCP/IP编程专家,故在此只是讲出了我的一点理解和体会。

  使用fsockopen函数打开一个Internet连接,函数语法格式:

int fsockopen(string hostname, int port, int [errno], string [errstr], int [timeout]);

  参数的意思我想不用讲了,这里由于要使用SMTP协议,所以端口号为25。在打开连接成功后,会返回一
个socket句柄,使用它就可以象使用文件句柄一样的。可使用的操作有fputs(),fgets(),feof(),fclose()
等。

  很简单地介绍就到这里吧。
SMTP的基础

  基于TCP/IP的因特网协议一般的命令格式都是通过请求/ 应答方式实现的,采用的都是文本信息,所以处理起来要轻易一些。SMTP是简单邮件传输协议的简称,它可以实现客户端向服务器发送邮件的功能。所以下面所讲的命令是指客户端向服务器发出请求指令,而响应则是指服务器返回给客户端的信息。

  SMTP分为命令头和信息体两部分。命令头主要完成客户端与服务器的连接,验证等。整个过程由多条命令组成。每个命令发到服务器后,由服务器给出响应信息,一般为3 位数字的响应码和响应文本。不同的服务器返回的响应码是遵守协议的,但是响应正文本则不必。每个命令及响应的最后都有一个回车符,这样使用fputs()和fgets()就可以进行命令与响应的处理了。SMTP的命令及响应信息都是单行的。信息体则是邮件的正文部分,最后的结束行应以单独的"."作为结束行。

  客户端一些常用的SMTP指令为:
HELO hostname: 与服务器打招呼并告知客户端使用的机器名字,可以随便填写
MAIL FROM: sender_id : 告诉服务器发信人的地址
RCPT TO: receiver_id : 告诉服务器收信人的地址
DATA : 下面开始传输信件内容,且最后要以只含有.的非凡行结束
RESET: 取消刚才的指令,从新开始
VERIFY userid: 校验帐号是否存在(此指令为可选指令,服务器可能不支持)
QUIT : 退出连接,结束

  服务器返回的响应信息为(格式为:响应码 空格 解释):
220 服务就绪(在socket连接成功时,会返回此信息)
221 正在处理
250 请求邮件动作正确,完成(HELO,MAIL FROM,RCPT TO,QUIT指令执行成功会返回此信息)
354 开始发送数据,结束以 .(DATA指令执行成功会返回此信息,客户端应发送信息)
500 语法错误,命令不能识别
550 命令不能执行,邮箱无效
552 中断处理:用户超出文件空间

  下面给出一个简单的命令头(这是在打开socket之后做的),是我向stmp.263.net发邮件的测试结果:
HELO limodou
250 smtp.263.net
MAIL FROM: chatme@263.net
250 Ok
RCPT TO: chatme@263.net
250 Ok
DATA
354 End data with .
To: chatme@263.net
From: chatme@263.net
Subject: test
From: chatme@263.net
test
.
QUIT
250 Ok: queued as C46411C5097E0

  这就是一些SMTP的简单知识。相关内容可以查阅RFC。

RFC 821定义了收/发电子邮件的相关指令。
RFC 822则制定了邮件


<?php 
// 请求 PHPmailer类 文件
require_once("class.phpmailer.php"
);  
//发送Email函数
function smtp_mail $sendto_email$subject$body$extra_hdrs$user_name
) { 
$mail = new PHPMailer
();  
$mail->IsSMTP();                                     
// send via SMTP  
$mail->Host "smtp.163.com";                       
// SMTP servers  
$mail->SMTPAuth true;                             
// turn on SMTP authentication  
$mail->Username "cfc4n";                          
// SMTP username     注意:普通邮件认证不需要加 @域名 
$mail->Password "123456";                         
// SMTP password  
$mail->From "cfc4n@163.com";                      
// 发件人邮箱 
$mail->FromName "中国资金管理网";                 
//   发件人 ,比如 中国资金管理网
$mail->CharSet "GB2312";                          
// 这里指定字符集! 
$mail->Encoding "base64"
;  
$mail->AddAddress($sendto_email,$user_name);        
// 收件人邮箱和姓名 
$mail->AddReplyTo("treasurer@treasurer.org.cn","中国资金管理网"
);  
//$mail->WordWrap = 50; // set word wrap  
//$mail->AddAttachment("/var/tmp/file.tar.gz");                                                    // attachment  附件1
//$mail->AddAttachment("/tmp/image.jpg", "new.jpg");                                         //附件2
$mail->IsHTML(true);                               
// send as HTML  
$mail->Subject $subject
;                        
// 邮件内容      可以直接发送html文件
$mail->Body 
= <<<EOT
<html><head> 
<meta http-equiv="Content-Language" c> 
<meta http-equiv="Content-Type" c>
<body>
{$user_name}
你好,欢迎您注册本站用户!
</body>
</html>
EOT;
$mail->AltBody ="text/html"
;  
if(
$mail->Send
())  
        {  
   
info_write("ok.txt","$user_name发送成功"
);
        }  
else { 
   
info_write("falied.txt","$user_name失败,错误信息$mail->ErrorInfo"
);
     } 

// 发送Email函数结束
// 写入发送结果函数
function info_write($filename,$info_log
)
    {
  
$info.= $info_log
;
  
$info.="\r\n"
;
  
$fp fopen ($filename,a
);
  
fwrite($fp,$info
);
  
fclose($fp
);
}
//定时跳转页面 函数  其中 1000是时间,1秒, 您可以自定义
function redirect($url
)
    {
         echo 
"<script>
    function redirect() {
     window.location.replace('$url');
    }
    window.setTimeout('redirect();', 15000);
     </script>"
;
    }
//读取文本 邮件地址  您也可以读 数据库
        
$filename "email.txt"
;
        
$fp fopen($filename,"r"
);
        
$contents fread($fp,filesize($filename
));
        
$num_email=explode("\r\n",$contents
);         
  
$len=count($num_email
);
        
fclose($fp
);
// 参数说明(发送到, 邮件主题, 邮件内容, 附加信息, 用户名)
$i $_GET['action'
];
        
$i
++;  
if (
$i<$len
)
    {
  
$rs=explode("@",$num_email[$i
]);
  
$user_name $rs['0'
];
  echo 
"正在发送第{$i}封({$num_email[$i]})邮件......"
;
        
smtp_mail($num_email[$i], 'Treasury Online周刊第十期'$body'http://www.treasurer.org.cn/'$user_name
);
        
redirect("?action=$i"
);
    }
else {
     echo 
"邮件全部发送完毕"
;
  exit;
      }
?>

原代码如下:

set_time_limit(120);
class smtp_mail
{
var $host; //主机
var $port; //端口 一般为25
var $user; //smtp认证的帐号
var $pass; //认证密码
var $debug = false; //是否显示和服务器会话信息?
var $conn;
var $result_str; //结果
var $in; //客户机发送的命令
var $from; //源信箱
var $to; //目标信箱
var $subject; //主题
var $body; //内容
function smtp_mail($host,$port,$user,$pass,$debug=false)
{
$this->host = $host;
$this->port = $port;
$this->user = base64_encode($user);
$this->pass = base64_encode($pass);
$this->debug = $debug;
$this->socket = socket_create (AF_INET, SOCK_STREAM, SOL_TCP); //具体用法请参考手册
if($this->socket)
{
$this->result_str = "创建socket:".socket_strerror(socket_last_error());
$this->debug_show($this->result_str);
}
else
{
exit("初始化失败,请检查您的网络连接和参数");
}
$this->conn = socket_connect($this->socket,$this->host,$this->port);
if($this->conn)
{
$this->result_str = "创建socket连接:".socket_strerror(socket_last_error());
$this->debug_show($this->result_str);
}
else
{
exit("初始化失败,请检查您的网络连接和参数");
}
$this->result_str = "服务器应答:<font color=#cc0000>".socket_read ($this->socket, 1024)."</font>";
$this->debug_show($this->result_str);


}
function debug_show($str)
{
if($this->debug)
{
echo $str."<p>rn";
}
}
function send($from,$to,$subject,$body)
{
if($from == "" || $to == "")
{
exit("请输入信箱地址");
}
if($subject == "") $sebject = "无标题";
if($body == "") $body = "无内容";
$this->from = $from;
$this->to = $to;
$this->subject = $subject;
$this->body = $body;

$all = "from:".$this->from."n";
$all .= "to:".$this->to."n";
$all .= "subject:".$this->subject."n";
$all .= $this->body;
/*
如过把$all的内容再加处理,就可以实现发送mime邮件了
不过还需要加很多程序
*/


//以下是和服务器会话
$this->in = "ehlo helorn";
$this->docommand();

$this->in = "auth loginrn";
$this->docommand();

$this->in = $this->user."rn";
$this->docommand();

$this->in = $this->pass."rn";
$this->docommand();

$this->in = "mail from:".$this->from."rn";
$this->docommand();

$this->in = "rcpt to:".$this->to."rn";
$this->docommand();

$this->in = "datarn";
$this->docommand();

$this->in = $all."rn.rn";
$this->docommand();

$this->in = "quitrn";
$this->docommand();

//结束,关闭连接

}
function docommand()
{
socket_write ($this->socket, $this->in, strlen ($this->in));
$this->debug_show("客户机命令:".$this->in);
$this->result_str = "服务器应答:<font color=#cc0000>".socket_read ($this->socket, 1024)."</font>";
$this->debug_show($this->result_str);
}
}
//这个是我做的测试,我用的是smtp.163.com,那你的信箱也必须是163.com的,要不人家不让你发!!
//你用这个类的时候你修改成你自己的信箱就可以了
$smtp = new smtp_mail("smtp.163.com","25","t_design","000000",true);
//如果你需要显示会话信息,请将上面的修改成
//$smtp = new smtp_mail("smtp.163.com","25","你的163.com的帐号","你的密码",true);
$smtp->send("t_design@163.com","t_beijing@yahoo.com.cn","你好","你好");
?>

PHP SMTP类

<?
/***********************************
PHP MIME SMTP ver 1.0 Powered by Boss_ch, Unigenius soft ware co. Ltd
All rights reserved, Copyright 2000 ;
本类用 PHP 通过 smtp  sock 操作发送 MIME 类型的邮件,可以发送
HTML 格式的正文、附件,采用 base64 编码
本版本是针对个人的发送,与多人群发版本不同的是,每发送到一个人,就重新进行一次编码,在接收端的用户看来,只是发送给他一个人的。
针对多人群发的情况,只发送一次,通过多个 RCPT TO 命令发送到不同的人信箱中,
说明:
请把 $hostname 设为你有权限的 默认 smtp 服务器或是在 new 时指定
把 $charset 改成你的默认 字符集
Html 正文中如有图片,请用绝对路径的引用 "httP://host/path/image.gif";
  并连上网,以保证程序能读取到图片的数据信息
假如是通过表单提交过来的 Html 正文,请先用 StripSlashes($html_body) 把正文内容进行预处理
  Html 中用到的样式表文件,请不要用 <link >之类 的引用,直接把样式表定义放在
<style></style>标签中

*************************************/
if(!isset($__smtp_class__)){
$__smtp_class__=1;

class smtp
{
var $hostname="";
var $port=25;
var $connection=0;
var $debug=1;

var $timeout=30;
var $err_str;
var $err_no;

var $autocode=true;
var $charset="??????";
var $subject="";
var $body="";
var $attach="";
var $temp_text_body;
var $temp_html_body;
var $temp_body_images;

var $bound_begin="=====powered_by_boss_chen_";
var $bound_end="_046484063883_=====";

Function smtp($server="smtp.china.com",$port=25,$time_out=20)
{$this->hostname=$server;
$this->port=$port;
$this->timeout=$time_out;
return true;
}

Function outdebug($message)
{
echo htmlspecialchars($message)."<br>n";
}


function command($command,$return_lenth=1,$return_code='2')
{
if ($this->connection==0)
{
$this->err_str="没有连接到任何服务器,请检查网络连接";
return false;
}
if ($this->debug)
$this->outdebug(">>> $command");
if (!fputs($this->connection,"$command rn"))
{
$this->err_str="无法发送命令".$command;
return false;
}
else
{
$resp=fgets($this->connection,256);
if($this->debug)
$this->outdebug("$resp");

最近有一个读者问我关于ThickBook新闻列表中处理订阅/退订的机制,并且是如何与实际的发送信息相配合,给邮件列表中的所有收信人的。问题提的不错,有一个相当轻易的方法,但是我首先要说的是我所做的没有任何投机取巧的地方,因为我非常的酷----就是有,我使用这个方法也只是因为我懒。注重,在市面上有非常多的邮件列表软件,可以把它装在系统上,并且也有一些别的,更多的是结合在一起的脚本集,你 也可以用来做同样的事情。但是,正如我所说,我是一个懒人,不想下载和安装任何东西,所以我生成了几 个相当简单的页面,用来执行我所要求的工作。很希望,有人能从中学到一些东西。

首先是订阅/退订脚本,它所做的工作就是从数据库表中增加或删除记录。把它叫做"manage.phtml" 或 差不多的什么东西。这样呢,就需要后台是某种数据库,在上面可以创建订阅表。根据政治中的半数原则,所以我将使用MySQL作为这个例子的数据库。你可以使用任何你常用的数据库,只是据PHP手册替换正确的数据库相关函数。

在我的订阅表中,我使用了两个字段:邮件地址(email_addr)和添加日期(date_added)。你可以根据需要增加字段,或者将date_added字段删除。在这个例子中,我只是向你展示我做了什么,你可以适当的进行 修改。在我的订阅表中,email_addr字段是一个不重复字段,意味着你不能增加另一个与之完全一样的e-mail 地址。这个可以避免重复订阅,而且当用户想退订时,也使删除记录的方法变得简单和可靠。

那么,让我们创建订阅/退订表单吧(manager.phtml或你想起的什么名字)。我使用同一个文件处理订阅和退订,也包括表格自身的动作,所以可能有点复杂。我将从头到尾讲解,然后把所有的片段组合在一起,放在一个表单中。

在脚本的开始处,是打开数据库和预备时间戳。在开始时处理这些不显眼的东西总是可以为我减轻一点压力。

--------------------------------------------------------------------------------
// 连接数据库
$db = mysql_connect("servername", "username", "password") or die ("不能连接。");

mysql_select_db("yourDB", $db) or die ("不能选择数据库。");

// 得到时间戳
$add_date = date("Y-m-d"); --------------------------------------------------------------------------------
  我们希望$op的值是"ds"。它不是复杂的程序缩写----我创的,表示"do something(做某事)" 。所以,脚本的第一件事就是查看$op的值是不是等于"ds"。这个值只有当表单被提交后才会被发送上来。所以假如$op的值不是"ds",那么说明用户还没有看过表单,所以应该把表单显示出来:

--------------------------------------------------------------------------------
if ($op != "ds") {

//需要订阅/退订

$text_block = "

<form name="form" method=post action="$PHP_SELF">
<input type=hidden name=op value=ds>

<p><strong>your e-mail address:</strong><br>
<input type=text name="email" size=25></p>

<p><strong>action:</strong><br>
<input type=radio name="action" value="sub" checked> sub
<input type=radio name="action" value="unsub"> unsub</p>

<P><input type=submit name="submit" value="do it"></p>
</form>

";

}--------------------------------------------------------------------------------
你会注重到我把文本放在$text_block变量中。通过把文本放在一个变量中,接下来我所要做的就是在后面在主HTML模板内输出$text_block的值。这是个人习惯问题,你可以根据你喜欢的时间和方式输出文本。

这个表单的动作是$PHP_SELF ,可以想象一下,它的意思就是当按下了提交按钮之后,它将会被重新装入。然后,你可以看到这个表单有三个字段:一个隐藏字段,用于给$op赋值为"ds" ;一个文本字段,叫做"email",在这里用户将填入他或她的email地址;还有一个是单选按钮集,叫做"action",根据它,用户可以决定执行哪一个动作(订阅或退订)。

在表单被提交之后,$op将等于"ds",并且$action的值将包含"sub"或"unsub"。那么,我们继续看上面 的if...语句,一旦提交,它将被跳过(因为$op=="ds")。假如$op的值为"ds"并且$action的值"sub"(订阅),下面的else if...句被执行。这段代码检查e-mail是否已经存在于订阅表中,假如不存在则将其插入到表中并打印出响应,否则忽略。

--------------------------------------------------------------------------------
else if (($op == "ds") && ($action == "sub")) {

// 检查邮件还未提交则提交它们,否则返回信息

$check = "select email_addr from subscribers
where email_addr = "$email"";

$check_result = mysql_query($check)
or die("不能执行地e-mail地址的检查。");

$check_num = mysql_num_rows($check_result);


if ($check_num == 0) {

// 假如$check_num为0,则没有找到匹配的记录,用户应该被提交

$sql = "insert into subscribers
values("$email", "$add_date")";


@mysql_query($sql) or die ("Couldn't insert email.");

$text_block = "
<P>感谢您的注册!</p>

[!--infotagslink--]

相关文章

  • NodeJS实现阿里大鱼短信通知发送

    本文给大家介绍的是nodejs实现使用阿里大鱼短信API发送消息的方法和代码,有需要的小伙伴可以参考下。...2016-01-20
  • c# socket网络编程接收发送数据示例代码

    这篇文章主要介绍了c# socket网络编程,server端接收,client端发送数据,大家参考使用吧...2020-06-25
  • PHP测试成功的邮件发送案例

    mail()函数的作用:连接到邮件服务器,利用smtp协议,与该服务器交互并投邮件。注意:1、mail函数不支持esmtp协议,---即,只能直投,不能登陆2、由上条,我们只能直投至最终的收件服务器地址.而该地址,又是在PHP.ini中指定的,所...2015-10-30
  • 详解JS WebSocket断开原因和心跳机制

    这篇文章主要介绍了JS WebSocket断开原因和心跳机制,对websocket感兴趣的同学,可以参考下...2021-05-08
  • php邮件发送的两种方式

    这篇文章研究的主要内容就是使用PHP来发送电子邮件,总结为以下两种方法:一、使用PHP内置的mail()函数<&#63;php $to = "test@163.com"; //收件人 $subject = "Test"; //主题 $message = "This is a test mail!"; //正文...2015-10-30
  • C#实现Socket通信的解决方法

    这篇文章主要介绍了C#实现Socket通信的解决方法,需要的朋友可以参考下...2020-06-25
  • 详解C# Socket异步通信实例

    本篇文章主要介绍了C# Socket异步通信,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • c# 实现发送邮件的功能

    这篇文章主要介绍了c# 如何实现发送邮件的功能,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
  • C# Socket的TCP通讯的实例代码

    本篇文章主要介绍了C# Socket的TCP通讯,socket通讯方式有两种:同步和异步,详细的介绍了这两种方法,有兴趣的可以了解一下。...2020-06-25
  • python使用socket高效传输视频数据帧(连续发送图片)

    本文主要介绍了python使用socket高效传输视频数据帧(连续发送图片),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-23
  • php邮件发送的两种方式

    这篇文章研究的主要内容就是使用PHP来发送电子邮件,总结为以下两种方法:一、使用PHP内置的mail()函数<&#63;php $to = "test@163.com"; //收件人 $subject = "Test"; //主题 $message = "This is a test mail!"; //正文...2015-10-30
  • C语言中send()函数和sendto()函数的使用方法

    这篇文章主要介绍了C语言中send()函数和sendto()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下...2020-04-25
  • JS如何实现基于websocket的多端桥接平台

    我们在调试过程使用的工具有:modheader,postman等,但这些工具都会存在的问题:缺少客户端里相应的设备信息;即使将cookie信息复制出来,也是存在过期的问题;多个设备之间切换时不方便;针对这些存在的问题,我基于websocket双向通信的特点,实现了多端桥接管理平台...2021-05-15
  • C# 实现WebSocket服务端教程

    这篇文章主要介绍了C# 实现WebSocket服务端教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-08
  • python实现企业微信定时发送文本消息的实例代码

    这篇文章主要介绍了python实现企业微信定时发送文本消息的实例代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-25
  • PHP测试成功的邮件发送案例

    mail()函数的作用:连接到邮件服务器,利用smtp协议,与该服务器交互并投邮件。注意:1、mail函数不支持esmtp协议,---即,只能直投,不能登陆2、由上条,我们只能直投至最终的收件服务器地址.而该地址,又是在PHP.ini中指定的,所...2015-10-30
  • C# Socket编程实现简单的局域网聊天器的示例代码

    这篇文章主要介绍了C# Socket编程实现简单的局域网聊天器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 一篇文章带你使用SpringBoot基于WebSocket的在线群聊实现

    这篇文章主要介绍了一篇文章带你使用SpringBoot基于WebSocket的在线群聊实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-08
  • Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例

    这篇文章主要介绍了Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例,帮助大家更好的理解和学习vue,感兴趣的朋友可以了解下...2020-11-18
  • Go语言实现socket实例

    这篇文章主要介绍了Go语言实现socket的方法,实例分析了socket客户端与服务器端的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-05-03