C#实现SMTP服务器,使用TCP命令发送Email

来源:互联网 发布:开淘宝网店货源怎么找 编辑:程序博客网 时间:2024/04/30 08:37

 using system;
using system.text;
using system.io;
using system.net;
using system.net.sockets;
using system.collections;

namespace skydev.web.mail
{
public enum mailformat{text,html};
public enum mailpriority{low=1,normal=3,high=5};

#region class mailattachments
public class mailattachments
{
private const int maxattachmentnum=10;
private ilist _attachments;

public mailattachments()
{
_attachments=new arraylist();
}

public string this[int index]
{
get { return (string)_attachments[index];}
}
/// <summary>
/// 添加邮件附件
/// </summary>
/// <param name="filepath">附件的绝对路径</param>
public void add(params string[] filepath)
{
if(filepath==null)
{
throw(new argumentnullexception("非法的附件"));
}
else
{
for(int i=0;i<filepath.length;i++)
{
add(filepath[i]);
}
}
}

/// <summary>
/// 添加一个附件,当指定的附件不存在时,忽略该附件,不产生异常。
/// </summary>
/// <param name="filepath">附件的绝对路径</param>
public void add(string filepath)
{
//当附件存在时才加入,否则忽略
if (system.io.file.exists(filepath))
{
if (_attachments.count<maxattachmentnum)
{
_attachments.add(filepath);
}
}
}

public void clear()//清除所有附件
{
_attachments.clear();
}

public int count//获取附件个数
{
get { return _attachments.count;}
}

}
#endregion//end class mailattachments



#region class mailmessage
/// <summary>
/// mailmessage 表示smtp要发送的一封邮件的消息。
/// </summary>
public class mailmessage
{
private const int maxrecipientnum=10;
public mailmessage()
{
_recipients=new arraylist();//收件人列表
_attachments=new mailattachments();//附件
_bodyformat=mailformat.text;//缺省的邮件格式为text
_priority=mailpriority.normal;
_charset="gb2312";
}

/// <summary>
/// 设定语言代码,默认设定为gb2312,如不需要可设置为""
/// </summary>
public string charset
{
get { return _charset;}
set { _charset=value;}
}

public string from
{
get{ return _from;}
set { _from=value;}
}

public string fromname
{
get { return _fromname;}
set { _fromname=value;}
}
public string body
{
get { return _body;}
set { _body=value;}
}

public string subject
{
get { return _subject;}
set { _subject=value;}
}

public mailattachments attachments
{
get {return _attachments;}
set { _attachments=value;}
}

public mailpriority priority
{
get { return _priority;}
set { _priority=value;}
}

public ilist recipients
{
get { return _recipients;}
}
/// <summary>
/// 增加一个收件人地址
/// </summary>
/// <param name="recipient">收件人的email地址</param>
public void addrecipients(string recipient)
{
//先检查邮件地址是否符合规范
if (_recipients.count<maxrecipientnum)
{
_recipients.add(recipient);//增加到收件人列表
}
}

public void addrecipients(params string[] recipient)
{
if (recipient==null)
{
throw (new argumentexception("收件人不能为空."));
}
else
{
for (int i=0;i<recipient.length;i++)
{
addrecipients(recipient[i]);
}
}
}

public mailformat bodyformat
{
set { _bodyformat=value;}
get { return _bodyformat;}
}

private string _from;//发件人地址
private string _fromname;//发件人姓名
private ilist _recipients;//收件人
private mailattachments _attachments;//附件
private string _body;//内容
private string _subject;//主题
private mailformat _bodyformat;//邮件格式
private string _charset="gb2312";//字符编码格式
private mailpriority _priority;//邮件优先级
}
#endregion


#region class smtpmail
public class smtpserverhelper
{
private string crlf="/r/n";//回车换行

/// <summary>
/// 错误消息反馈
/// </summary>
private string errmsg;

/// <summary>
/// tcpclient对象,用于连接服务器
/// </summary>
private tcpclient tcpclient;

/// <summary>
/// networkstream对象
/// </summary>
private networkstream networkstream;

/// <summary>
/// 服务器交互记录
/// </summary>
private string logs="";

/// <summary>
/// smtp错误代码哈希表
/// </summary>
private hashtable errcodeht = new hashtable();

/// <summary>
/// smtp正确代码哈希表
/// </summary>
private hashtable rightcodeht = new hashtable();

public smtpserverhelper()
{
smtpcodeadd();//初始化smtpcode
}

~smtpserverhelper()
{
networkstream.close();
tcpclient.close();
}

/// <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)
{
//建立文件流对象
system.io.filestream filestr=new system.io.filestream(filepath,system.io.filemode.open);
byte[] by=new byte[system.convert.toint32(filestr.length)];
filestr.read(by,0,by.length);
filestr.close();
return(system.convert.tobase64string(by));
}

/// <summary>
/// smtp回应代码哈希表
/// </summary>
private void smtpcodeadd()
{
//[rfc 821 4.2.1.]
/*
4.2.2. numeric order list of reply codes

211 system status, or system help reply
214 help message
[information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user]
220 <domain> service ready
221 <domain> service closing transmission channel
250 requested mail action okay, completed
251 user not local; will forward to <forward-path>

354 start mail input; end with <crlf>.<crlf>

421 <domain> service not available,
closing transmission channel
[this may be a reply to any command if the service knows it
must shut down]
450 requested mail action not taken: mailbox unavailable
[e.g., mailbox busy]
451 requested action aborted: local error in processing
452 requested action not taken: insufficient system storage

500 syntax error, command unrecognized
[this may include errors such as command line too long]
501 syntax error in parameters or arguments
502 command not implemented
503 bad sequence of commands
504 command parameter not implemented
550 requested action not taken: mailbox unavailable
[e.g., mailbox not found, no access]
551 user not local; please try <forward-path>
552 requested mail action aborted: exceeded storage allocation
553 requested action not taken: mailbox name not allowed
[e.g., mailbox syntax incorrect]
554 transaction failed

*/

errcodeht.add("421","服务未就绪,关闭传输信道");
errcodeht.add("432","需要一个密码转换");
errcodeht.add("450","要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)");
errcodeht.add("451","放弃要求的操作;处理过程中出错");
errcodeht.add("452","系统存储不足,要求的操作未执行");
errcodeht.add("454","临时认证失败");
errcodeht.add("500","邮箱地址错误");
errcodeht.add("501","参数格式错误");
errcodeht.add("502","命令不可实现");
errcodeht.add("503","服务器需要smtp验证");
errcodeht.add("504","命令参数不可实现");
errcodeht.add("530","需要认证");
errcodeht.add("534","认证机制过于简单");
errcodeht.add("538","当前请求的认证机制需要加密");
errcodeht.add("550","要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)");
errcodeht.add("551","用户非本地,请尝试<forward-path>");
errcodeht.add("552","过量的存储分配,要求的操作未执行");
errcodeht.add("553","邮箱名不可用,要求的操作未执行(例如邮箱格式错误)");
errcodeht.add("554","传输失败");


/*
211 system status, or system help reply
214 help message
[information on how to use the receiver or the meaning of a
particular non-standard command; this reply is useful only
to the human user]
220 <domain> service ready
221 <domain> service closing transmission channel
250 requested mail action okay, completed
251 user not local; will forward to <forward-path>

354 start mail input; end with <crlf>.<crlf>
*/

rightcodeht.add("220","服务就绪");
rightcodeht.add("221","服务关闭传输信道");
rightcodeht.add("235","验证成功");
rightcodeht.add("250","要求的邮件操作完成");
rightcodeht.add("251","非本地用户,将转发向<forward-path>");
rightcodeht.add("334","服务器响应验证base64字符串");
rightcodeht.add("354","开始邮件输入,以<crlf>.<crlf>结束");

}

/// <summary>
/// 发送smtp命令
/// </summary>
private bool sendcommand(string str)
{
byte[]writebuffer;
if(str==null||str.trim()==string.empty)
{
return true;
}
logs+=str;
writebuffer = encoding.default.getbytes(str);
try
{
networkstream.write(writebuffer,0,writebuffer.length);
}
catch
{
errmsg="网络连接错误";
return false;
}
return true;
}

/// <summary>
/// 接收smtp服务器回应
/// </summary>
private string recvresponse()
{
int streamsize;
string returnvalue = string.empty;
byte[] readbuffer = new byte[1024] ;
try
{
streamsize=networkstream.read(readbuffer,0,readbuffer.length);
}
catch
{
errmsg="网络连接错误";
return "false";
}

if (streamsize==0)
{
return returnvalue ;
}
else
{
returnvalue = encoding.default.getstring(readbuffer).substring(0,streamsize);
logs+=returnvalue+this.crlf;
return returnvalue;
}
}

/// <summary>
/// 与服务器交互,发送一条命令并接收回应。
/// </summary>
/// <param name="str">一个要发送的命令</param>
/// <param name="errstr">如果错误,要反馈的信息</param>
private bool dialog(string str,string errstr)
{
if(str==null||str.trim()==string.empty)
{
return true;
}
if(sendcommand(str))
{
string rr=recvresponse();
if(rr=="false")
{
return false;
}
//检查返回的代码,根据[rfc 821]返回代码为3位数字代码如220
string rrcode=rr.substring(0,3);
if(rightcodeht[rrcode]!=null)
{
return true;
}
else
{
if(errcodeht[rrcode]!=null)
{
errmsg+=(rrcode+errcodeht[rrcode].tostring());
errmsg+=crlf;
}
else
{
errmsg+=rr;
}
errmsg+=errstr;
return false;
}
}
else
{
return false;
}
}


/// <summary>
/// 与服务器交互,发送一组命令并接收回应。
/// </summary>

private bool dialog(string[] str,string errstr)
{
for(int i=0;i<str.length;i++)
{
if(!dialog(str[i],""))
{
errmsg+=crlf;
errmsg+=errstr;
return false;
}
}

return true;
}


//连接服务器
private bool connect(string smtpserver,int port)
{
//创建tcp连接
try
{
tcpclient=new tcpclient(smtpserver,port);
}
catch(exception e)
{
errmsg=e.tostring();
return false;
}
networkstream=tcpclient.getstream();

//验证网络连接是否正确
if(rightcodeht[recvresponse().substring(0,3)]==null)
{
errmsg="网络连接失败";
return false;
}
return true;
}

private string getprioritystring(mailpriority mailpriority)
{
string priority="normal";
if (mailpriority==mailpriority.low)
{
priority="low";
}
else if (mailpriority==mailpriority.high)
{
priority="high";
}
return priority;
}

/// <summary>
/// 发送电子邮件,smtp服务器不需要身份验证
/// </summary>
/// <param name="smtpserver"></param>
/// <param name="port"></param>
/// <param name="mailmessage"></param>
/// <returns></returns>
public bool sendemail(string smtpserver,int port,mailmessage mailmessage)
{
return sendemail(smtpserver,port,false,"","",mailmessage);
}

/// <summary>
/// 发送电子邮件,smtp服务器需要身份验证
/// </summary>
/// <param name="smtpserver"></param>
/// <param name="port"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="mailmessage"></param>
/// <returns></returns>
public bool sendemail(string smtpserver,int port,string username,string password,mailmessage mailmessage)
{
return sendemail(smtpserver,port,false,username,password,mailmessage);
}

private bool sendemail(string smtpserver,int port,bool esmtp,string username,string password,mailmessage mailmessage)
{
if (connect(smtpserver,port)==false)//测试连接服务器是否成功
return false;

string priority=getprioritystring(mailmessage.priority);
bool html=(mailmessage.bodyformat==mailformat.html);

string[] sendbuffer;
string sendbufferstr;

//进行smtp验证,现在大部分smtp服务器都要认证
if(esmtp)
{
sendbuffer=new string[4];
sendbuffer[0]="ehlo " + smtpserver + crlf;
sendbuffer[1]="auth login" + crlf;
sendbuffer[2]=base64encode(username) + crlf;
sendbuffer[3]=base64encode(password) + crlf;
if(!dialog(sendbuffer,"smtp服务器验证失败,请核对用户名和密码。"))
return false;
}
else
{//不需要身份认证
sendbufferstr="helo " + smtpserver + crlf;
if(!dialog(sendbufferstr,""))
return false;
}

//发件人地址
sendbufferstr="mail from:<" + mailmessage.from + ">" + crlf;
if(!dialog(sendbufferstr,"发件人地址错误,或不能为空"))
return false;

//收件人地址
sendbuffer=new string[mailmessage.recipients.count];
for(int i=0;i<mailmessage.recipients.count;i++)
{
sendbuffer[i]="rcpt to:<" +(string)mailmessage.recipients[i] +">" + crlf;
}
if(!dialog(sendbuffer,"收件人地址有误"))
return false;

/*
sendbuffer=new string[10];
for(int i=0;i<recipientbcc.count;i++)
{

sendbuffer[i]="rcpt to:<" + recipientbcc[i].tostring() +">" + crlf;

}

if(!dialog(sendbuffer,"密件收件人地址有误"))
return false;
*/

sendbufferstr="data" + crlf;
if(!dialog(sendbufferstr,""))
return false;

//发件人姓名
sendbufferstr="from:" + mailmessage.fromname + "<" +mailmessage.from +">" +crlf;

//if(replyto.trim()!="")
//{
// sendbufferstr+="reply-to: " + replyto + crlf;
//}

//sendbufferstr+="to:" + toname + "<" + recipient[0] +">" +crlf;
//至少要有一个收件人
if (mailmessage.recipients.count==0)
{
return false;
}
else
{
sendbufferstr += "to:=?"+mailmessage.charset.toupper()+"?b?"+
base64encode((string)mailmessage.recipients[0])+"?="+"<"+(string)mailmessage.recipients[0]+">"+crlf;
}

//sendbufferstr+="cc:";
//for(int i=0;i<recipient.count;i++)
//{
// sendbufferstr+=recipient[i].tostring() + "<" + recipient[i].tostring() +">,";
//}
//sendbufferstr+=crlf;

sendbufferstr+=
((mailmessage.subject==string.empty || mailmessage.subject==null)?"subject:":((mailmessage.charset=="")?("subject:" +
mailmessage.subject):("subject:" + "=?" + mailmessage.charset.toupper() + "?b?" +
base64encode(mailmessage.subject) +"?="))) + crlf;
sendbufferstr+="x-priority:" + priority + crlf;
sendbufferstr+="x-msmail-priority:" + priority + crlf;
sendbufferstr+="importance:" + priority + crlf;
sendbufferstr+="x-mailer: lion.web.mail.smtpmail pubclass [cn]" + crlf;
sendbufferstr+="mime-version: 1.0" + crlf;
if(mailmessage.attachments.count!=0)
{
sendbufferstr+="content-type: multipart/mixed;" + crlf;
sendbufferstr += " boundary=/"====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_")+"=====/""+crlf+crlf;
}

if(html)
{
if(mailmessage.attachments.count==0)
{
sendbufferstr += "content-type: multipart/alternative;"+crlf;//内容格式和分隔符
sendbufferstr += " boundary=/"=====003_dragon520636771063_=====/""+crlf+crlf;
sendbufferstr += "this is a multi-part message in mime format."+crlf+crlf;
}
else
{
sendbufferstr +="this is a multi-part message in mime format."+crlf+crlf;
sendbufferstr += "--=====001_dragon520636771063_====="+crlf;
sendbufferstr += "content-type: multipart/alternative;"+crlf;//内容格式和分隔符
sendbufferstr += " boundary=/"=====003_dragon520636771063_=====/""+crlf+crlf;
}
sendbufferstr += "--=====003_dragon520636771063_====="+crlf;
sendbufferstr += "content-type: text/plain;"+ crlf;
sendbufferstr += ((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +

mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode("邮件内容为html格式,请选择html方式查看") + crlf + crlf;

sendbufferstr += "--=====003_dragon520636771063_====="+crlf;


sendbufferstr+="content-type: text/html;" + crlf;
sendbufferstr+=((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +
mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode(mailmessage.body) + crlf + crlf;
sendbufferstr += "--=====003_dragon520636771063_=====--"+crlf;
}
else
{
if(mailmessage.attachments.count!=0)
{
sendbufferstr += "--=====001_dragon303406132050_====="+crlf;
}
sendbufferstr+="content-type: text/plain;" + crlf;
sendbufferstr+=((mailmessage.charset=="")?(" charset=/"iso-8859-1/""):(" charset=/"" +
mailmessage.charset.tolower() + "/"")) + crlf;
sendbufferstr+="content-transfer-encoding: base64" + crlf + crlf;
sendbufferstr+= base64encode(mailmessage.body) + crlf;
}

//sendbufferstr += "content-transfer-encoding: base64"+crlf;

if(mailmessage.attachments.count!=0)
{
for(int i=0;i<mailmessage.attachments.count;i++)
{
string filepath = (string)mailmessage.attachments[i];
sendbufferstr += "--====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_") +"====="+crlf;
//sendbufferstr += "content-type: application/octet-stream"+crlf;
sendbufferstr += "content-type: text/plain;"+crlf;
sendbufferstr += " name=/"=?"+mailmessage.charset.toupper()+"?b?"+
base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+crlf;
sendbufferstr += "content-transfer-encoding: base64"+crlf;
sendbufferstr += "content-disposition: attachment;"+crlf;
sendbufferstr += " filename=/"=?"+mailmessage.charset.toupper()+"?b?"+
base64encode(filepath.substring(filepath.lastindexof("//")+1))+"?=/""+crlf+crlf;
sendbufferstr += getstream(filepath)+crlf+crlf;
}
sendbufferstr += "--====="+
(html?"001_dragon520636771063_":"001_dragon303406132050_")+"=====--"+crlf+crlf;
}

sendbufferstr += crlf + "." + crlf;//内容结束

if(!dialog(sendbufferstr,"错误信件信息"))
return false;

sendbufferstr="quit" + crlf;
if(!dialog(sendbufferstr,"断开连接时错误"))
return false;

networkstream.close();
tcpclient.close();
return true;
}
}



public class smtpmail
{
private static string _smtpserver;

/// <summary>
/// 格式:smtpaccount:password@smtpserveraddress<br>
/// 或者:smtpserveraddress<br>
/// <code>
/// smtpmail.smtpserver="user:12345678@smtp.126.com";
/// //或者:
/// smtpmail.smtpserver="smtp.126.com";
/// 或者:
/// smtpmail.smtpserver=smtpserverhelper.getsmtpserver("user","12345678","smtp.126.com");
/// </code>
/// </summary>
public static string smtpserver
{
set { _smtpserver=value;}
get { return _smtpserver;}
}

public static bool send(mailmessage mailmessage,string username,string password)
{
smtpserverhelper helper=new smtpserverhelper();
return helper.sendemail(_smtpserver,25,username,password,mailmessage);
}

}

#endregion
}




using system;
using nunit.framework;


namespace skydev.web.mail
{
/// <summary>
/// test 的摘要说明。
/// </summary>
[testfixture]
public class testsmtpmail
{
//安装测试用例,完成初始化操作
[setup]
public void setup()
{
}

//测试结束完成清理操作
[teardown]
public void teardown()
{

}

[test]
public void testmailattachments()
{
skydev.web.mail.mailattachments attachments=new mailattachments();
assert.areequal(0,attachments.count,"初始化mailattachments");
attachments.add("c://autoexec.bat");
assert.areequal(1,attachments.count,"增加附件(附件确实存在)");
attachments.add("c://autoexec.dat.txt");
assert.areequal(1,attachments.count,"增加附件(附件不存在)");
attachments.clear();
assert.areequal(0,attachments.count,"清除附件");
}

[test]
public void testmailmessage()
{
mailmessage message=new mailmessage();
assert.areequal(0,message.attachments.count,"初始化mailattachments");
assert.areequal(mailformat.text,message.bodyformat,"邮件格式");
assert.areequal("gb2312",message.charset,"缺省的字符集");
}

[test]
public void testsendmail()
{
smtpmail.smtpserver="smtp.126.com";
mailmessage mail=new mailmessage();
mail.from="qs1976@126.com";
mail.fromname="曾青松";
mail.addrecipients("qs1976@126.com");
mail.subject="主题:测试邮件";
mail.bodyformat=mailformat.text;
mail.body="测试的内容.";
mail.attachments.add("c://test.txt");
smtpmail.send(mail,"","");//请填写自己的测试邮件帐号
}
}
}

【reprinted from http://www.west263.com thanks】

【刚在百度空间的 “半开半闭”发现一篇类似的博文,格式比较好些,就不贴出来了】

原创粉丝点击