Asp.net在System.Web.Mail名称空间中有一个发送email的内建类,但这仅是cdosys的一个假象。开发者能使用一个替代的它smtp邮件服务。在这篇文章里面,我将会展示如何创建一个用于asp.net的功能齐全的smtp邮件服务。
首先,我们创建一个继承命名空间System.Net.Sockets的TcpClient类的类。TcpClient类提供简单的方法用于连接,发送,接收网络的数据流。GetStream方法用于创建一个网络流(NetworkStream)。读和写网络流(NetworkStream)的方法用于发送数据给远程主机和从远程主机接收网络流。
public class ClientConnection : TcpClient
{
private NetworkStream _NetworkStream = null;
private StreamReader _StreamReader = null;
private StreamWriter _StreamWriter = null;
public void Initialise()
{
_NetworkStream = this.GetStream();
_StreamReader = new StreamReader(_NetworkStream,
System.Text.Encoding.Default, false, this.ReceiveBufferSize);
_StreamWriter = new StreamWriter(_NetworkStream,
System.Text.Encoding.Default, this.SendBufferSize);
}
public void Send(string s)
{
_StreamWriter.WriteLine(s);
_StreamWriter.Flush();
}
public string Read()
{
return _StreamReader.ReadLine();
}
}
接下来,我们创建发送邮件出去的类,这个类有几个属性去设置关于被发送的邮件的一些信息。
public string MailServer = "127.0.0.1" ;
public string From = "" ;
public string To = "" ;
public string Body = "" ;
public string Subject = "" ;
和一个发送邮件的方法。这个方法将会用服务器名和它的端口去创建一个连接。指令可以被发送到远程主机。
public void Send()
{
tcp = new ClientConnection();
tcp.Connect(MailServer,25);
tcp.Initialise();
SendCommandToServer("HELO " + System.Net.Dns.GetHostName());
SendCommandToServer("MAIL FROM: " + From + "\r\n");
SendCommandToServer("RCPT TO: " + To + "\r\n");
string StrHeaders = "";
StrHeaders += "From: " + From + "\r\n";
StrHeaders += "To: " + To + "\r\n";
StrHeaders += "Subject: " + Subject + "\r\n";
StrHeaders += "Content-type: text/plain; charset=\"ISO-8859-1\"" + "\r\n";
SendCommandToServer("DATA\r\n" + StrHeaders);
SendCommandToServer(Body + "\r\n.\r\n");
SendCommandToServer("QUIT\r\n");
}
还有一个私有的送一个指令到本地服务器的方法。
private void SendCommandToServer(string cmd)
{
tcp.Send(cmd);
Response=tcp.Read();
System.Web.HttpContext.Current.Trace.Warn("Response",Response);
}
现在,这个类基本完成了。开发者还可以建立错误回执和释放资源(用TcpClient类中的Close()方法)。同样,这几个属性可以被扩展到在邮件中包含更多的信息。
最后,我们可以在aspx文件中写一些发送邮件的代码。在这个例子中,"localhost"是邮件服务器。你可以根据情况改变这个,或者可以看我的关于如何设置你的发送邮件的本地服务器的文章。
<%@ Import Namespace="MyComponents" %>
<script runat="server">
protected void Page_Load(Object Src, EventArgs E)
{
if (IsPostBack)
{
Mail Mailer=new Mail();
Mailer.From=From.Text;
Mailer.To=To.Text;
Mailer.Subject=Subject.Text;
Mailer.Body=Body.Text;
Mailer.MailServer="localhost";
Mailer.Send();
}
}
</script>
下面是完整的代码。
using System.IO;
using System.Net.Sockets;
namespace MyComponents
{
public class ClientConnection : TcpClient
{
private NetworkStream _NetworkStream = null;
private StreamReader _StreamReader = null;
private StreamWriter _StreamWriter = null;
public void Initialise()
{
_NetworkStream = this.GetStream();
_StreamReader = new StreamReader(_NetworkStream,
System.Text.Encoding.Default, false, this.ReceiveBufferSize);
_StreamWriter = new StreamWriter(_NetworkStream,
System.Text.Encoding.Default, this.SendBufferSize);
}
public void Send(string s)
{
_StreamWriter.WriteLine(s);
_StreamWriter.Flush();
}
public string Read()
{
return _StreamReader.ReadLine();
}
}
public class Mail
{
public string MailServer = "127.0.0.1" ;
public string From = "" ;
public string To = "" ;
public string Body = "" ;
public string Subject = "" ;
private ClientConnection tcp=null;
private string Response="";
public void Send()
{
tcp = new ClientConnection();
tcp.Connect(MailServer,25);
tcp.Initialise();
SendCommandToServer("HELO " + System.Net.Dns.GetHostName());
SendCommandToServer("MAIL FROM: " + From + "\r\n");
SendCommandToServer("RCPT TO: " + To + "\r\n");
string StrHeaders = "";
StrHeaders += "From: " + From + "\r\n";
StrHeaders += "To: " + To + "\r\n";
StrHeaders += "Subject: " + Subject + "\r\n";
StrHeaders += "Content-type: text/plain; charset=\"ISO-8859-1\"" + "\r\n";
SendCommandToServer("DATA\r\n" + StrHeaders);
SendCommandToServer(Body + "\r\n.\r\n");
SendCommandToServer("QUIT\r\n");
}
private void SendCommandToServer(string cmd)
{
tcp.Send(cmd);
Response=tcp.Read();
System.Web.HttpContext.Current.Trace.Warn("Response",Response);
}
}
}
补充:
SMTP协议原始命令码和工作原理
1.SMTP是工作在两种情况下:一是电子邮件从客户机传输到服务器;二是从某一个服务器传输到另一个服务器
2.SMTP是个请求/响应协议,命令和响应都是基于ASCII文本,并以CR和LF符结束。响应包括一个表示返回状态的三位数字代码
3.SMTP在TCP协议25号端口监听连接请求
4.连接和发送过程:
a.建立TCP连接
b.客户端发送HELO命令以标识发件人自己的身份,然后客户端发送MAIL命令
服务器端正希望以OK作为响应,表明准备接收
c.客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行
服务器端则表示是否愿意为收件人接受邮件
d.协商结束,发送邮件,用命令DATA发送
e. 以.表示结束输入内容一起发送出去
f.结束此次发送,用QUIT命令退出。
5. 邮件路由过程:
SMTP服务器基于‘域名服务DNS中计划收件人的域名来路由电子邮件。SMTP服务器基于DNS中的MX记录来路由电子邮件,MX记录注册了域名和相关的SMTP中继主机,属于该域的电子邮件都应向该主机发送。
在c#下发送邮件:
主要用:
类MailAttachment 提供用于构造电子邮件附件的属性和方法。
类 MailMessage 提供用于构造电子邮件属性和方法。
类 SmtpMail 提供用于Windows2000的 协作数据对象(cdosys)消息组件来发送消息的属性和方法。
枚举 MailCoding 提供电子邮件编码的枚举值
枚举 MailFormat 提供电子邮件格式的枚举值
枚举 MailPriority 提供电子邮件优先级的枚举值
// 下面是发送的主要代码
public void click(object sender,System.EventArgs e)
{
MailMessage mailObj = new MailMessage();
//发送者地址
mailObj.From = mailfrom.Text;
//目的地地址
mailObj.To = mailto.Text;
//发送的标题
mailObj.Subject = mailsubject1.Text;
//发送的内容
mailObj.Body = mailobj.Text;
//发送的优先级
mailObj.Priority = MailPriority.High;
Label9.Text = RadioButtonList1.SelectedValue;
//发送的格式
if(RadioButtonList1.SelectedValue=="html")
{
mailObj.BodyFormat = MailFormat.Html;
}
else if(RadioButtonList1.SelectedValue=="text")
{
mailObj.BodyFormat = MailFormat.Text;
}
//发送文件
if(inputMailAttachment.PostedFile.ContentLength>0)
{
mailObj.Attachments.Add(new MailAttachment(inputMailAttachment.PostedFile.FileName));
}
SmtpMail.Send(mailObj);
}
在Visual Stdio.Net下发送成功.
用C#接受邮件:
主要用到了两个类:TcpClient类和NetworkStream类。
- TcpClient类是运用C#进行网络编程的一个非常重要的类,它提供了通过网络连接、发送和接收数据的简单方法,从而实现了网络编程的大大简化。TcpClient 类提供了一些简单的方法,用于在同步阻塞模式下通过网络来连接、发送和接收流数据。使 TcpClient 连接并交换数据,使用 TCP ProtocolType 创建的 TcpListener 或 Socket 必须侦听是否有传入的连接请求。可以使用下面两种方法之一连接到该侦听器:创建一个 TcpClient,并调用三个可用的 Connect 方法之一。
- 使用远程主机的主机名和端口号创建 TcpClient。此构造函数将自动尝试一个连接。
注意 如果要在同步阻塞模式下发送无连接数据报,请使用 UdpClient 类。
对继承者的说明: 要发送和接收数据,请使用 GetStream 方法来获取一个 NetworkStream。调用 NetworkStream 的 Write 和 Read 方法与远程主机之间发送和接收数据。使用 Close 方法释放与 TcpClient 关联的所有资源。
NetworkStream类实现通过网络套接字发送和接收数据的标准.Net框架流机制,它支持对网络数据流的同步和异步访问,是实现网络通讯的重要组成部分。NetworkStream 类提供在阻塞模式下通过 Stream 套接字发送和接收数据的方法。您可以使用 NetworkStream 类进行同步和异步数据传输。要创建 NetworkStream,必须提供连接的 Socket。也可指定 NetworkStream 对所提供的 Socket 具有哪些 FileAccess 权限。默认情况下,关闭 NetworkStream 并不会关闭所提供的 Socket。将 Write 和 Read 方法用于简单的单线程同步阻塞 I/O。若要使用不同的线程来处理 I/O,则请考虑使用 BeginWrite/ EndWrite 和 BeginRead/ EndRead 方法进行通信。
NetworkStream 不支持对网络数据流的随机访问。CanSeek 属性用于指示流是否支持查找,它的值始终为 false。读取 Position 属性、读取 Length 属性或者调用 Seek 方法都会引发 NotSupportedException。
/////消息响应函数完成了到POP3服务器的连接工作,并根据用户名和密码进行登录
//用110端口新建POP3服务器连接
Server = new TcpClient(PopServer.Text,110);
Status.Items.Clear();
Try
{
//初始化
NetStrm = Server.GetStream();
RdStrm= new StreamReader(Server.GetStream());
Status.Items.Add(RdStrm.ReadLine());
//登录服务器过程
Data = "USER "+ Username.Text+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
Data = "PASS "+ Password.Text+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
//向服务器发送STAT命令,从而取得邮箱的相关信息:邮件数量和大小
Data = "STAT"+CRLF;
szData = System.Text.Encoding.ASCII.GetBytes(Data.ToCharArray());
NetStrm.Write(szData,0,szData.Length);
Status.Items.Add(RdStrm.ReadLine());
}
catch(InvalidOperationException err)
{
Status.Items.Add("Error: "+err.ToString());
}
}
用C#访问Hotmail
Hotmail是世界上影响最广的Web邮件系统,遗憾的是,当我们要为Hotmail编写独立的客户程序(不通过浏览器访问的客户程序)时,马上就会遇到Hotmail不提供POP网关这一障碍。
Hotmail信箱默认的HTTPMail网关在http://services.msn.com/svcs/hotmail/httpmail.asp。HTTPMail协议实际上是一个标准的WebDAV服务,只不过尚未公开而已。在编写C#程序时,我们可以方便地调用.NET框架在System.Net名称空间中提供的各个TCP和HTTP类。另外,由于我们要操作WebDAV,在C#环境下利用XMLHTTP连接Hotmail最为简便,只需引用一下MSXML2组件就可以直接访问。