在为公司写通知服务时,从网上找到了以上地址,非常感谢原作者创造性的劳动。改写的目的是为了适应作为服务运行的要求:
1、适应多线程的要求,发送邮件服务可在后台运行,将与smtp服务器的连接视为独占资源。
2、适应稳定性的要求,不再以简单地抛出异常来处理错误,在出现异常后等待一定时间间隔后重试,重试一段时间间隔后若还时发不出去,则认为是smtp出错,返回发送邮件不成功的标识。
3、精简属性、方法,与邮件相关的信息不再作为属性,而是作为send的参数传入;只公布了一个无重载的send方法。以此类为基类,另写通知服务要求的接口方法。
以下是改写后的代码:
using system;
using system.text;
using system.io;
using system.net;
using system.net.sockets;
using system.collections;
using system.threading;
namespace deep.sendemail
{
#region aspnetpager server control
/// <summary>
/// 邮件可以通过 microsoft windows 2000 中内置的 smtp 邮件服务或任意 smtp 服务器来传送
/// </summary>
public class smtpmail
{
private const string enter="/r/n";
/// <summary>
/// 设定语言代码,默认设定为gb2312,如不需要可设置为""
/// </summary>
private string m_charset="gb2312";
/// <summary>
/// 服务器交互记录
/// </summary>
private stringbuilder m_logs = new stringbuilder();
private string m_errcode;
/// <summary>
/// smtp错误代码哈希表
/// </summary>
private hashtable m_errcodeht = new hashtable();
/// <summary>
/// smtp正确代码哈希表
/// </summary>
private hashtable m_rightcodeht = new hashtable();
/// <summary>
/// 最多收件人数量
/// </summary>
private int m_recipientmaxnum = 2;
/// <summary>
/// 重复时间,以秒为单位
/// </summary>
private int m_repeattime = 120;
/// <summary>
/// 服务器出错或拒绝后的等待时间,以毫秒为单位
/// </summary>
private int m_waittime = 20000;
/// <summary>
/// 初始化 <see cref="lion.web.mail.smtpmail"/> 类的新实例
/// </summary>
public smtpmail()
{
smtpcodeadd();
}
#region properties 定义属性
/// <summary>
/// 服务器交互记录,如发现本组件不能使用的smtp服务器,请将出错时的logs发给我([email protected]),我将尽快查明原因。
/// </summary>
public string logs
{
get
{
return m_logs.tostring();
}
}
/// <summary>
/// 最多收件人数量
/// </summary>
public int recipientmaxnum
{
set
{
m_recipientmaxnum = value;
}
get
{
return m_recipientmaxnum;
}
}
/// <summary>
/// 设定语言代码,默认设定为gb2312,如不需要可设置为""
/// </summary>
public string charset
{
get
{
return this.m_charset;
}
set
{
this.m_charset = value;
}
}
/// <summary>
/// 重复时间,以秒为单位
/// </summary>
public int repeattime
{
get {return m_repeattime;}
set {m_repeattime = value;}
}
/// <summary>
/// 服务器出错或拒绝后的等待时间,以毫秒为单位
/// </summary>
public int waittime
{
get {return m_waittime;}
set {m_waittime = value > 10000?value:10000;}
}
#endregion
#region methods 定义方法
/// <summary>
/// 邮件服务器域名和验证信息
/// 形如:"user:[email protected]:25",也可省略次要信息。如"user:[email protected]"或"www.server.com"
/// </summary>
/// <param name="maildomain">输入用户名、密码、邮件服务器域名、端口号</param>
/// <param name="mailserver">返回邮件服务器域名</param>
/// <param name="mailserverusername">返回用户名</param>
/// <param name="password">返回密码</param>
/// <param name="mailserverport">返回端口号</param>
/// <param name="needsmtp">返回是否需要smtp验证</param>
/// <returns></returns>
private bool setmaildomain(string maildomain,out string mailserver,out string mailserverusername,out string password,
out int mailserverport,out bool needsmtp)
{
bool isright = false;
//为输出变量赋初值
mailserver = string.empty;
mailserverusername = string.empty;
password = string.empty;
mailserverport = 25;
needsmtp = false;
mailserver = maildomain.trim();
int tempint;
if( mailserver != "" )
{
tempint = mailserver.indexof("@");
isright = true;
if(tempint!=-1)
{
string str = mailserver.substring(0,tempint);
mailserverusername = str.substring(0,str.indexof(":"));
password = str.substring(str.indexof(":")+1,str.length-str.indexof(":")-1);
needsmtp = !(password==string.empty);
mailserver = maildomain.substring(tempint+1,maildomain.length-tempint-1);
}
tempint = mailserver.indexof(":");
if(tempint != -1)
{
mailserverport = system.convert.toint32(mailserver.substring(tempint+1,mailserver.length-tempint-1));
mailserver = mailserver.substring(0,tempint);
}
}
return isright;
}
/// <summary>
/// 添加邮件附件
/// </summary>
/// <param name="filepath">附件绝对路径</param>
private ilist addattachment(params string[] filepath)
{
if(filepath == null || filepath.length == 0)
{
return null;
}
ilist m_attachments = new system.collections.arraylist();// 邮件附件列表
for(int i=0;i<filepath.length;i++)
{
if(file.exists(filepath[i]))
{
m_attachments.add(filepath[i]);
}
else
{
m_logs.append("错误:没找到文件名为"+filepath[i]+"的附件文件!"+enter);
}
}
return m_attachments;
}
/// <summary>
/// 添加一组收件人(不超过m_recipientmaxnum个),参数为字符串数组
/// </summary>
/// <param name="recipients">保存有收件人地址的字符串数组(不超过m_recipientmaxnum个)</param>
private hashtable addrecipient(params string[] recipients)
{
if(recipients==null || recipients.length == 0)
{
return null;
}
hashtable recipientlist=new hashtable();// 收件人列表
for(int i=0;i<recipients.length;i++)
{
string recipient = recipients[i].trim();
if(recipient !=string.empty && recipient.indexof("@") != -1)
{
recipientlist.add(recipientlist.count,recipients[i]);
}
}
return recipientlist;
}
/// <summary>
/// 发送邮件方法
/// </summary>
/// <param name="smtpserver">smtp服务器信息,如"username:[email protected]:25",也可去掉部分次要信息,如"www.smtpserver.com"</param>
/// <param name="from">发件人mail地址</param>
/// <param name="fromname">发件人姓名</param>
/// <param name="to">收件人地址列表</param>
/// <param name="toname">收件人姓名</param>
/// <param name="html">是否html邮件</param>
/// <param name="subject">邮件主题</param>
/// <param name="body">邮件正文</param>
/// <param name="filepath">邮件附件列表</param>
public bool send(string smtpserver,string from,string fromname,string[] recipientadd,string recipientname,bool ishtml,string subject,priority priority, string body,string[] filepath)
{
//如果收件人多于服务器可同时发送的最大值,则分多次发送
if(recipientadd.length > recipientmaxnum)
{
string[] recipientadd1 = new string[recipientmaxnum];
string[] recipientadd2 = new string[recipientadd.length - recipientmaxnum];
for(int i = 0;i < recipientadd.length; i++)
{
if(i < recipientmaxnum)
{
recipientadd1[i] = recipientadd[i];
}
else
{
recipientadd2[i - recipientmaxnum] = recipientadd[i];
}
}
return send(smtpserver,from,fromname,recipientadd1,recipientname,ishtml,subject,priority, body,filepath)
&&
send(smtpserver,from,fromname,recipientadd2,recipientname,ishtml,subject,priority, body,filepath);
}
if(m_logs.length > 2048)
{
m_logs.remove(0,m_logs.length);
}
string mailserver="";// 邮件服务器域名
int mailserverport=25;// 邮件服务器端口号
string username="";// smtp认证时使用的用户名
string password="";// smtp认证时使用的密码
bool needsmtp=false;// 是否需要smtp验证
setmaildomain(smtpserver,out mailserver,out username,out password,
out mailserverport,out needsmtp);
if(mailserver.trim()=="")
{
m_logs.append("必须指定smtp服务器"+enter);
return false;
}
ilist attachments = addattachment(filepath);// 邮件附件列表
hashtable recipients = addrecipient(recipientadd);// 收件人列表
if(recipients == null || recipients.count == 0 )
{
m_logs.append("必须指定收件人"+enter);
return false;
}
if(recipients.count > recipientmaxnum)
{
m_logs.append("一次发送的收件人太多"+enter);
return false;
}
bool issuccessful = false;
lock(this)
{
tcpclient tcpclientobject = null;// tcpclient对象,用于连接服务器
networkstream networkstreamobject = null;// networkstream对象
datetime datetimebegin = datetime.now;
int usetime = 0;
while(! ( usetime > repeattime || issuccessful))
{
try
{
tcpclientobject=new tcpclient(mailserver,mailserverport);
networkstreamobject = tcpclientobject.getstream();
issuccessful =sendemail(networkstreamobject,needsmtp,mailserver,username,password,recipients,from,
fromname,recipientname,subject,priority.tostring(),attachments, ishtml, body);
}
catch(exception e)
{
m_logs.append("错误:"+e.message+enter);
}
finally
{
if(networkstreamobject!=null)networkstreamobject.close();
if(tcpclientobject!=null)tcpclientobject.close();
if(!issuccessful)
{
string n = thread.currentthread.name;
thread.sleep(waittime);
}
usetime = ((timespan)(datetime.now - datetimebegin)).seconds;
}
}
}
return issuccessful;
}
#endregion
#region private helper functions
/// <summary>
/// smtp回应代码哈希表
/// </summary>
private void smtpcodeadd()
{
m_errcodeht.add("500","邮箱地址错误");
m_errcodeht.add("501","参数格式错误");
m_errcodeht.add("502","命令不可实现");
m_errcodeht.add("503","服务器需要smtp验证");
m_errcodeht.add("504","命令参数不可实现");
m_errcodeht.add("421","服务未就绪,关闭传输信道");
m_errcodeht.add("450","要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)");
m_errcodeht.add("550","要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)");
m_errcodeht.add("451","放弃要求的操作;处理过程中出错");
m_errcodeht.add("551","用户非本地,请尝试<forward-path>");
m_errcodeht.add("452","系统存储不足,要求的操作未执行");
m_errcodeht.add("552","过量的存储分配,要求的操作未执行");
m_errcodeht.add("553","邮箱名不可用,要求的操作未执行(例如邮箱格式错误)");
m_errcodeht.add("432","需要一个密码转换");
m_errcodeht.add("534","认证机制过于简单");
m_errcodeht.add("538","当前请求的认证机制需要加密");
m_errcodeht.add("454","临时认证失败");
m_errcodeht.add("530","需要认证");
m_rightcodeht.add("220","服务就绪");
m_rightcodeht.add("250","要求的邮件操作完成");
m_rightcodeht.add("251","用户非本地,将转发向<forward-path>");
m_rightcodeht.add("354","开始邮件输入,以<enter>.<enter>结束");
m_rightcodeht.add("221","服务关闭传输信道");
m_rightcodeht.add("334","服务器响应验证base64字符串");
m_rightcodeht.add("235","验证成功");
}
/// <summary>
/// 将字符串编码为base64字符串
/// </summary>
/// <param name="str">要编码的字符串</param>
private string base64encode(string str)
{
byte[] barray;
barray=encoding.default.getbytes(str);
return convert.tobase64string(barray);
}
/// <summary>
/// 将base64字符串解码为普通字符串
/// </summary>
/// <param name="str">要解码的字符串</param>
private string base64decode(string str)
{
byte[] barray;
barray=convert.frombase64string(str);
return encoding.default.getstring(barray);
}
/// <summary>
/// 得到上传附件的文件流
/// </summary>
/// <param name="filepath">附件的绝对路径</param>
private string getstream(string filepath)
{
byte[] by = null;
system.io.filestream filestr = null;
string streamstring = "";
try
{
//建立文件流对象
filestr=new system.io.filestream(filepath,system.io.filemode.open);
by=new byte[system.convert.toint32(filestr.length)];
filestr.read(by,0,by.length);
streamstring = system.convert.tobase64string(by);
}
catch(exception ex)
{
//写错误日志
m_logs.append("错误:"+ex.message+enter);
}
finally
{
if(filestr != null)
{
filestr.close();
}
}
return streamstring;
}
/// <summary>
/// 发送smtp命令
/// </summary>
private bool sendcommand(string str,networkstream _networkstreamobject)
{
byte[] writebuffer;
if(str==null||str.trim()==string.empty)
{
return true;
}
m_logs.append(str+enter);
writebuffer = encoding.default.getbytes(str);
try
{
_networkstreamobject.write(writebuffer,0,writebuffer.length);
}
catch(exception ex)
{
//写日志
m_logs.append("错误:"+ex.message+enter);
return false;
}
return true;
}
/// <summary>
/// 接收smtp服务器回应
/// </summary>
private string recvresponse(networkstream _networkstreamobject)
{
int streamsize = 0;
string returnvalue = string.empty;
byte[] readbuffer = new byte[1024] ;
try
{
streamsize = _networkstreamobject.read(readbuffer,0,readbuffer.length);
}
catch(exception ex)
{
//写日志
m_logs.append("错误:"+ex.message+enter);
m_errcode = ex.message;
return "false";
}
if (streamsize==0)
{
return returnvalue ;
}
else
{
returnvalue = encoding.default.getstring(readbuffer).substring(0,streamsize);
m_logs.append(returnvalue+enter);
return returnvalue;
}
}
/// <summary>
/// 与服务器交互,发送一条命令并接收回应。
/// </summary>
/// <param name="str">一个要发送的命令</param>
private bool dialog(string str,networkstream _networkstream)
{
if(str==null||str.trim()=="")
{
return true;
}
if(sendcommand(str,_networkstream))
{
string rr=recvresponse(_networkstream);
if(rr=="false")
{
return false;
}
string rrcode=rr.substring(0,3);
if(m_rightcodeht[rrcode]!=null)
{
return true;
}
else
{
m_errcode = rrcode;
return false;
}
}
else
{
return false;
}
}
/// <summary>
/// 与服务器交互,发送一组命令并接收回应。
/// </summary>
private bool dialog(string[] str,networkstream _networkstream)
{
for(int i=0;i<str.length;i++)
{
if(!dialog(str[i],_networkstream))
{
return false;
}
}
return true;
}
/// <summary>
/// sendemail
/// </summary>
/// <returns></returns>
private bool sendemail(networkstream _networkstream,bool needsmtp,string mailserver,string username,string password,hashtable recipients,string from,
string fromname,string recipientname,string subject,string priority,ilist attachments,bool ishtml,
string body)
{
//验证网络连接是否正确
if(m_rightcodeht[recvresponse(_networkstream).substring(0,3)]==null)
{
return false;
}
string[] sendbuffer;
string sendbufferstr;
stringbuilder sendbufferstrbuilder = new stringbuilder();
//进行smtp验证
if(needsmtp)
{
sendbuffer=new string[4];
sendbuffer[0]="ehlo " + mailserver + enter;
sendbuffer[1]="auth login" + enter;
sendbuffer[2]=base64encode(username) + enter;
sendbuffer[3]=base64encode(password) + enter;
if(!dialog(sendbuffer,_networkstream))
{
return false;
}
}
else
{
sendbufferstr="helo " + mailserver + enter;
if(!dialog(sendbufferstr,_networkstream))
return false;
}
//
sendbufferstr="mail from:<" + from + ">" + enter;
if(!dialog(sendbufferstr,_networkstream))
return false;
//
sendbuffer=new string[m_recipientmaxnum];
for(int i=0;i<recipients.count;i++)
{
sendbuffer[i]="rcpt to:<" + recipients[i].tostring() +">" + enter;
}
if(!dialog(sendbuffer,_networkstream))
return false;
sendbufferstr="data" + enter;
if(!dialog(sendbufferstr,_networkstream))
return false;
sendbufferstrbuilder.append("from:" + fromname + "<" + from +">" +enter);
sendbufferstrbuilder.append("to:=?"+charset.toupper()+"?b?"+base64encode(recipientname)+"?="+"<"+recipients[0]+">"+enter);
sendbufferstrbuilder.append("cc:");
for(int i=0;i<recipients.count;i++)
{
sendbufferstrbuilder.append(recipients[i].tostring() + "<" + recipients[i].tostring() +">,");
}
sendbufferstrbuilder.append(enter);
sendbufferstrbuilder.append(((subject==string.empty || subject==null)?"subject:":((charset=="")?("subject:" + subject):("subject:" + "=?" + charset.toupper() + "?b?" + base64encode(subject) +"?="))) + enter);
sendbufferstrbuilder.append("x-priority:" + priority + enter);
sendbufferstrbuilder.append("x-msmail-priority:" + priority + enter);
sendbufferstrbuilder.append("importance:" + priority + enter);
sendbufferstrbuilder.append("x-mailer: lion.web.mail.smtpmail pubclass [cn]" + enter);
sendbufferstrbuilder.append("mime-version: 1.0" + enter);
if(attachments != null && attachments.count!=0)
{
sendbufferstrbuilder.append("content-type: multipart/mixed;" + enter);
sendbufferstrbuilder.append(" boundary=/"====="+(ishtml?"001_dragon520636771063_":"001_dragon303406132050_")+"=====/""+enter+enter);
}
if(ishtml)
{
if(attachments != null && attachments.count==0)
{
sendbufferstrbuilder.append("content-type: multipart/alternative;"+enter);//内容格式和分隔符
sendbufferstrbuilder.append(" boundary=/"=====003_dragon520636771063_=====/""+enter+enter);
sendbufferstrbuilder.append("this is a multi-part message in mime format."+enter+enter);
}
else
{
sendbufferstrbuilder.append("this is a multi-part message in mime format."+enter+enter);
sendbufferstrbuilder.append("--=====001_dragon520636771063_====="+enter);
sendbufferstrbuilder.append("content-type: multipart/alternative;"+enter);//内容格式和分隔符
sendbufferstrbuilder.append(" boundary=/"=====003_dragon520636771063_=====/""+enter+enter);
}
sendbufferstrbuilder.append("--=====003_dragon520636771063_====="+enter);
sendbufferstrbuilder.append("content-type: text/plain;"+ enter);
sendbufferstrbuilder.append(((charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" + charset.tolower() + "/"")) + enter);
sendbufferstrbuilder.append("content-transfer-encoding: base64" + enter + enter);
sendbufferstrbuilder.append(base64encode("邮件内容为html格式,请选择html方式查看") + enter + enter);
sendbufferstrbuilder.append("--=====003_dragon520636771063_====="+enter);
sendbufferstrbuilder.append("content-type: text/html;" + enter);
sendbufferstrbuilder.append(((charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" + charset.tolower() + "/"")) + enter);
sendbufferstrbuilder.append("content-transfer-encoding: base64" + enter + enter);
sendbufferstrbuilder.append(base64encode(body) + enter + enter);
sendbufferstrbuilder.append("--=====003_dragon520636771063_=====--"+enter);
}
else
{
if(attachments != null && attachments.count!=0)
{
sendbufferstrbuilder.append("--=====001_dragon303406132050_====="+enter);
}
sendbufferstrbuilder.append("content-type: text/plain;" + enter);
sendbufferstrbuilder.append(((charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" + charset.tolower() + "/"")) + enter);
sendbufferstrbuilder.append("content-transfer-encoding: base64" + enter + enter);
sendbufferstrbuilder.append(base64encode(body) + enter);
}
//sendbufferstr += "content-transfer-encoding: base64"+enter;
if(attachments != null && attachments.count!=0)
{
for(int i=0;i<attachments.count;i++)
{
string filepath = (string)attachments[i];
sendbufferstrbuilder.append("--====="+ (ishtml?"001_dragon520636771063_":"001_dragon303406132050_") +"====="+enter);
//sendbufferstr += "content-type: application/octet-stream"+enter;
sendbufferstrbuilder.append("content-type: text/plain;"+enter);
sendbufferstrbuilder.append(" name=/"=?"+charset.toupper()+"?b?"+base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+enter);
sendbufferstrbuilder.append("content-transfer-encoding: base64"+enter);
sendbufferstrbuilder.append("content-disposition: attachment;"+enter);
sendbufferstrbuilder.append(" filename=/"=?"+charset.toupper()+"?b?"+base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+enter+enter);
sendbufferstrbuilder.append(getstream(filepath)+enter+enter);
}
sendbufferstrbuilder.append("--====="+ (ishtml?"001_dragon520636771063_":"001_dragon303406132050_") +"=====--"+enter+enter);
}
sendbufferstrbuilder.append(enter + "." + enter);
sendbufferstr = sendbufferstrbuilder.tostring();
if(!dialog(sendbufferstr,_networkstream))
return false;
sendbufferstr="quit" + enter;
if(!dialog(sendbufferstr,_networkstream))
return false;
return true;
}
#endregion
#region
/*
/// <summary>
/// 添加一个密件收件人
/// </summary>
/// <param name="str">收件人地址</param>
public bool addrecipientbcc(string str)
{
if(str==null||str.trim()=="")
return true;
if(recipientbccnum<10)
{
recipientbcc.add(recipientbccnum,str);
recipientbccnum++;
return true;
}
else
{
m_logs.append("错误:收件人过多");
return false;
}
}
/// <summary>
/// 添加一组密件收件人(不超过10个),参数为字符串数组
/// </summary>
/// <param name="str">保存有收件人地址的字符串数组(不超过10个)</param>
public bool addrecipientbcc(string[] str)
{
for(int i=0;i<str.length;i++)
{
if(!addrecipientbcc(str[i]))
{
return false;
}
}
return true;
}
*/
#endregion
}
/// <summary>
/// 邮件发送优先级
/// </summary>
public enum priority
{
high,
normal,
low
}
#endregion
}