jmail发信失败
在本地服务器上顺利执行,能正常发送邮件,
传到阿里云后出现以下错误
jmail.Message
错误 '8000ffff'
The message was undeliverable. All servers failed to receive the message
主机类型:独享虚拟主机经济版(美国)
贴上代码:
strMailAddress = "smtp.163.com"
strMailUser = "*****@163.com" '发件人用户名
strMailPass = "*******" '发件账户密码
Subject = "邮件标题" '邮件标题
Email = "*****@sina.com"
Sender = "Wu"
Content= "test"
Set JMail = Server.CreateObject("JMail.Message")
JMail.Charset = "gb2312" '邮件字符集,默认为"US-ASCII"
JMail.Silent=False
JMail.ContentType = "text/html" '邮件正文格式
JMail.MailServerUserName = strMailUser '发件服务器的用户名
JMail.MailServerPassword = strMailPass '发件服务器的密码
JMail.MailDomain = strMailAddress '域名
JMail.AddRecipient email '收信人
JMail.Subject = subject '主题
JMail.Body = content '邮件正文(纯文本格式)
JMail.FromName = sender '发送者姓名
JMail.From = strMailUser '发信人email
JMail.Priority = 3
JMail.Send(strMailAddress)
Windows Server 2008配置Jmail发送邮件
jmail是一个32位的邮件发送程序,在Windows 2008上注册后你会发现无法使用。注册组件可以通过asp.net探针来检查。
比如现在我的网站www.abc.com需要通过asp.net的jmail插件来发送邮件:
打开应用IIS管理器,打开应用程序池,找到www.abc.com,右击”高级设置“
将"启用32位应用程序"改为"true"
复制组件以及注册组件
组件位置 C:\Program Files (x86)\Dimac\w3JMail\jmail.dll
请不要把组件放在C:\windows\system32目录下,否则会提示失败!
注册组件
1
regsvr32 "C:\Program Files (x86)\Dimac\w3JMail\jmail.dll"
另外需要注意防火墙是否把端口给封禁了,比如mcafee杀毒软件就会将发送邮件进行封禁
附件:http://down.51cto.com/data/2367785
本文转自 rong341233 51CTO博客,原文链接:http://blog.51cto.com/fengwan/1786874
服务器Jmail配置问题
将内网的程序上传至外网后,出现“检索 COM 类工厂中 CLSID 为 {E5FF9F62-0E7C-4372-8AD5-DA7D2418070C} 的组件时失败,原因是出现以下错误: 80070005”。外网服务器也安装过Jmail,但根据报错,明显是组件没有被正确注册。利用Jmail的安装包,卸载、重新安装也不行,最后采用极端手段。将jmail.dll拷贝到服务器的system32目录下 ,开始-运行-CMD-输入regsvr32 jmail.dll
才算搞定。
本文转自Kai的世界,道法自然博客园博客,原文链接:http://www.cnblogs.com/kaima/archive/2008/05/28/1209186.html,如需转载请自行联系原作者。
使用JMail组件代替Sql Mail发送Email
54powerman(54powerman@163.com)
说两句:在某人最困难的时候,写下此文,寄给某某报想骗点稿费,结果石沉大海,若干年后,翻腾硬盘翻出来了。呵呵,或许可以对希望在SqlServer中发送邮件的网友有所帮助。54powerman^_^
Sql Mail技术给每一位数据库开发人员和DBA(数据库管理员)带来了极大的方便,利用该技术,Sql Server数据库代理程序可以在系统出现异常的时候自动发送Email通知管理员,开发人可以利用它让数据库自动定期的修改用户密码,然后发送Email通知用户……等等这些应用,都不同程度上把我们从繁杂的工作中解放出来。但是,Sql Mail的配置是比较复杂的,相信90%以上的人在配置Sql Mail的时候都遇到过各种各样的麻烦,至少有70%的人放弃了Sql Mail而选择其他方案来解决这个问题。笔者是一名Web开发人员,亲身经历了这一切,并找到了一个更好的替代方法。不敢独享,写出来以飨读者。Sql Mail配置有几种方式,按照支持软件可划分为基于Exchange、Outlook2000(以上)和第三方软件的配置方案,三种方式各有利弊,主要表现在以下几个方面: 使用Outlook客户端配合Sql Server实现Sql Mail 此方案软件要求较低,只需要在Sql Server所在服务器上安装Outlook2000以上版本客户端即可。它要求在Sql Mail使用期间,OutLook客户端必须打开,否则,只能到下次打开时,邮件才能发送出去。另外,如果服务器为远程服务器,用微软官方的远程桌面无法完成配置,可替代的方案是DBA亲自去机房直接操作,或者安装PcAnywhere10替代远程桌面进行操作。 使用Exchange要求较高 Microsoft推荐使用Exchange作为Sql Mail的最佳拍档,MSDN的资料提出:“由于 POP3/SMTP 协议存在的局限性和登录问题,Microsoft 建议您使用 Exchange Server 来实现可靠性”。但是Exchange并不是专门做这个来使用的,可以说是屈才了,而且Exchange要求服务器配置域管理器,相信这个东东对大多数数据库服务器来说用处不大,只不过是浪费资源罢了。如果我们要在多台服务器上配置Sql Mail那么就需要在每一台服务器上配置域管理器,或者所有的服务器都配置到一个域内,但是对于服务器比较分散的系统来说这样是不现实的。 使用第三方系统支持 微软MSDN称:“如果您使用的是第三方邮件服务器,则必须将邮件服务器配置为 POP3 服务器。如果这些邮件服务器使用的本地邮件服务可能是由第三方邮件客户端安装的,Microsoft 将不支持连接到这些服务器”。这就意味着你还要使用Windows平台的邮件服务,使用ASP编写网站的朋友一定都知道,Cdonts.dll组件实在是……。面对这些问题,笔者就变成了我刚才说的那70%了,虽然爬网无数,读文字数万把Sql Mail配置好了,但是我仍然绝对放弃,因为我不想在做第2台、第3台……的时候重蹈覆辙。替代的方案就是Jmail组件+OLE自动化对象,以上的问题迎刃而解。 预备知识 1.OLE自动化函数 OLE自动化使应用程序能够对另一个应用程序中实现的对象进行操作,或者将对象公开以便可以对其进行操作。自动化客户端是可对属于另一个应用程序的公开对象进行操作的应用程序,本文值得是Sql Server。公开对象的应用程序称为自动化服务器,又成为自动化组件,本文中即Jmail组件咯。客户端通过访问应用程序对象的属性和函数对这些对象进行操作。 在Sql Server使用Ole组件的途径是几个系统扩展存储过程sp_OACreate、sp_OADestroy、sp_OAGetErrorInfo、sp_OAMethod、sp_OASetProperty和sp_OAGetProperty,再次简单地介绍一下使用方法,详细资料参考Sql Server联机丛书。 OLE自动化对象的使用方法: (1)调用 sp_OACreate 创建对象。 格式:sp_OACreate clsid,objecttoken OUTPUT [ , context ] 参数:clsid——是要创建的 OLE 对象的程序标识符 (ProgID)。此字符串描述该 OLE 对象的类,其形式,如 'OLEComponent.Object',OLEComponent 是 OLE 自动化服务器的组件名称,Object 是 OLE 对象名,本文中使用的“JMail.Message”;Objecttoken——是返回的对象标志,并且必须是数据类型为 int 的局部变量。用于标识所创建的 OLE 对象,并将在调用其它 OLE 自动化存储过程时使用。本文中就是通过它来调用JMail.Message组件的属性和方法的。 Context——指定新创建的 OLE 对象要在其中运行的执行上下文。本文不使用该参数,故不赘述。以下与此一致,所有方法属性的其他用法请参阅Sql Server联机文档。 (2)使用该对象。 (a)调用 sp_OAGetProperty 获取属性值。 格式:_OAGetProperty objecttoken,propertyname [, propertyvalue OUTPUT] 参数:(前面出现过的参数,以下均省略。) Propertyname——对象的属性名称; Propertyvalue——返回的对象的属性值,该参数带OUTPUT属性,执行该操作后,你就可以从propertyvalue中得到属性的值了。 (b)调用 sp_OASetProperty 将属性设为新值。 格式:sp_OASetProperty objecttoken, propertyname, propertyvalue (c)调用 sp_OAMethod 以调用某个方法。 格式:sp_OAMethod objecttoken, methodname [, returnvalue OUTPUT] [ , [ parametername = ] parametervalue [...n]] 参数:Returnvalue——调用方法的返回值,如果没有返回值,此参数设置为NULL; Parametername——方法定义中的参数名称,也就是形参; Parametervalue——参数值; ……n——表示,可以带很多参数,个数由方法定义限制; (d)调用 sp_OAGetErrorInfo 获取最新的错误信息。 格式:sp_OAGetErrorInfo [objecttoken ] [, source OUTPUT] [, description OUTPUT] 参数:Source——错误源; Description——错误描述; (3)调用 sp_OADestroy 释放对象。 格式:sp_OADestroy objecttoken 2.xp_cmdshell扩展存储过程 该扩展存储过程在master数据库中,它的全路径是master..xp_cmdshell(注意,中间是2个点),它的功能是:以操作系统命令行解释器的方式执行给定的命令字符串,并以文本行方式返回任何输出。 格式:xp_cmdshell {'command_string'} [, no_output] 参数:'command_string'——是在操作系统命令行解释器上执行的命令字符串。 no_output——是可选参数,表示执行给定的 command_string,但不向客户端返回任何输出。本文应用中不使用该参数。 操作方法 (1)软件准备 请先到http://www.dimac.net/或者国内提供组件下载的网站下载最新版的JMail组件,如果你得到的是安装版,执行weJMailx.exe即可,系统的配置安装程序会自动完成。如果只有一个JMail.dll文件,请按照下面的步骤安装: (a)新建文本文件,输入如下命令: regsvr32 JMail.dll net start w3svc 另存为Install.Bat(注意,千万不要保存成Install.Bat.Txt啊) (b)此文件连同Jmail.dll一起拷贝到Sql Server数据库服务器的System32目录下,并执行双击Install.Bat即可。 (2)准备好了吗?跟我来吧 (a)运行Sql Server查询分析器,并以sa身份登录到Sql Server数据库; (b)如果你的存储过程要添加到YourDefaultCatalog数据库,请在空白Sql窗口输入如下指令,否则请相应修改数据库名。 Use YourDefaultCatalog 按F5或者运行按钮运行该指令; (c)创建基本发送存储过程 复制如下代码到Sql Server命令窗口,并运行。下面的代码中有相应的注释,文中不多做解释,如有疑问请查看前面的“预备知识”或者查询Sql Server帮助文件,当然也可以和作者联系。
Create Procedure dbo.sp_jmail_send@sender varchar(100),@sendername varchar(100)='',@serveraddress varchar(255)='SMTP服务器地址',@MailServerUserName varchar(255)=null,@MailServerPassword varchar(255)=null,@recipient varchar(255),@recipientBCC varchar(200)=null,@recipientBCCName varchar(200)=null,@recipientCC varchar(200)=null,@recipientCCName varchar(100)=null,@attachment varchar(100) =null,@subject varchar(255),@mailbody textAs/*该存储过程使用办公自动化脚本调用Dimac w3 JMail AxtiveX组件来代替Sql Mail发送邮件该方法支持“服务器端身份验证”*/--声明w3 JMail使用的常规变量及错误信息变量Declare @object int,@hr int,@rc int,@output varchar(400),@description varchar (400),@source varchar(400)
--创建JMail.Message对象
Exec @hr = sp_OACreate 'jmail.message', @object OUTPUT
--设置邮件编码Exec @hr = sp_OASetProperty @object, 'Charset', 'gb2312'
--身份验证If Not @MailServerUserName is nullExec @hr = sp_OASetProperty @object, 'MailServerUserName',@MailServerUserNameIf Not @MailServerPassword is nullExec @hr = sp_OASetProperty @object, 'MailServerPassword',@MailServerPassword
--设置邮件基本参数Exec @hr = sp_OASetProperty @object, 'From', @senderExec @hr = sp_OAMethod @object, 'AddRecipient', NULL , @recipientExec @hr = sp_OASetProperty @object, 'Subject', @subjectExec @hr = sp_OASetProperty @object, 'Body', @mailbody
--设置其它参数if not @attachment is nullexec @hr = sp_OAMethod @object, 'Addattachment', NULL , @attachment,'false'print @attachmentIf (Not @recipientBCC is null) And (Not @recipientBCCName is null)Exec @hr = sp_OAMethod @object, 'AddRecipientBCC', NULL , @recipientBCC,@recipientBCCNameElse If Not @recipientBCC is nullExec @hr = sp_OAMethod @object, 'AddRecipientBCC', NULL , @recipientBCC
If (Not @recipientCC is null) And (Not @recipientCCName is null)Exec @hr = sp_OAMethod @object, 'AddRecipientCC', NULL , @recipientCC,@recipientCCNameElse If Not @recipientCC is nullExec @hr = sp_OAMethod @object, 'AddRecipientCC', NULL , @recipientCC
If Not @sendername is nullExec @hr = sp_OASetProperty @object, 'FromName', @sendername
--调用Send方法发送邮件Exec @hr = sp_OAMethod @object, 'Send', null,@serveraddress
--捕获JMail.Message异常Exec @hr = sp_OAGetErrorInfo @object, @source OUTPUT, @description OUTPUT
if (@hr = 0)BeginSet @output='错误源: '+@sourcePrint @outputSelect @output = '错误描述: ' + @descriptionPrint @outputEndElseBeginPrint '获取错误信息失败!'ReturnEnd
--释放JMail.Message对象Exec @hr = sp_OADestroy @object
(d)简化存储过程操作,以适合我们平时的使用习惯 上面的存储过程基本可以完成邮件发送操作,但是非常冗长,而且不符合我们的习惯,比如它不支持多个发送给接收者、不支持将Sql指令运行结果以附件形式发送(这是Sql Mail的功能,我们也可以做到)等,所以我们要再写一个存储过程来调用它,以简化操作,并扩展功能。
Create Procedure SendMail@Sender varChar(50)=null,@strRecipients varChar(200),@strSubject varChar(200),@strMessage varChar(2000),@sql varChar(50)=null) As
Declare @SplitStr varchar(1) --定义邮件地址分割符变量Declare @strTemp varchar(200) --定义多个收件人字符串临时变量Declare @email varchar(50) --用分割符分割后的单个收件人字符串变量
Declare @SenderAddress varChar(50)Declare @Attach varChar(200)
Declare @DefaultSender varChar(50)Declare @MailServer varChar(50)Declare @User varChar(50)Declare @Pass varChar(50)Declare @SenderName varChar(50)Declare @AttachDir varChar(100)
--初始化缺省变量Set @DefaultSender='缺省发送地址'Set @MailServer='邮件服务器地址'Set @User='SMTP服务器验证用户地址'Set @Pass='SMTP服务器验证地址'Set @SenderName='缺省发件人名称'Set @AttachDir='E:/LOG/WebData/Jmail/'+Replace(Replace(Replace(Convert(varChar(19),GetDate(),120),'-',''),' ',''),':','')+'.txt'
--将Email地址分割符统一为分号set @SplitStr=';'Set @strTemp=@strRecipients+@SplitStr+'end'Set @strTemp=Replace(@strTemp,',',';')
--判断是否有sql语句If (@Sql is Null) Or (len(@Sql)=0)Set @AttachDir=NullElseBeginDeclare @CmdStr varChar(200)Set @CmdStr='bcp "'+@Sql+'" queryout '+@AttachDir+' -c'EXEC master..xp_cmdshell @CmdStrEnd
while CharIndex(@SplitStr,@strTemp,1)<>0BeginSet @email=left(@strTemp,CharIndex(@SplitStr,@strTemp,1)-1)Set @strTemp=right(@strTemp,len(@strTemp)-len(@email)-1)If (@Sender Is Null) Or (Len(@Sender)=0)Set @SenderAddress=@DefaultSenderElseSet @SenderAddress=@SenderPrint @email--调用sp_jmail_send发送邮件EXEC sp_jmail_send @sender=@SenderAddress,@sendername=@SenderName,@serveraddress=@MailServer,@MailServerUserName=@User,@MailServerPassword=@Pass,@recipient=@email,@subject=@strSubject,@mailbody=@strMessage,@attachment=@AttachDirEnd 此存储过程只扩展了Sql查询结果附件发送,如果你要发送标准附件,请直接使用sp_jmail_send存储过程或者自行扩展功能。
Csharp: jmail組件發送郵件
jmail.Message MailObj = new jmail.MessageClass();
MailObj.From = "geovindu@qq.com"; //发件人的地址 可以显示与服务名不一样
MailObj.FromName = "締友計算機信息技術有限公司";//用户名
//MailObj.FromName = "geovindu@dupcit.com";
MailObj.Logging = true;
MailObj.MailServerUserName = "geovindu@qq.com"; //发件人地址
MailObj.ReplyTo = "geovindu@163.com"; //回复邮件名称
MailObj.MailServerPassWord = "123456789"; //服务器验证
//MailObj.Text = "text/html";
MailObj.HTMLBody = "涂斯博,涂聚文,<br/>涂聚文"+DateTime.Now.ToString();//內容
MailObj.Charset = "gb2312";//編碼
MailObj.Subject = "缔友计算机信息技术有限公司項目解決方案事宜";//標題
MailObj.AddRecipient("463588883@qq.com", "Geovin Du", "A"); //添加接收人
MailObj.AddRecipientBCC("401620863@qq.com", "b"); //密抄
MailObj.AddRecipientCC("413265257@qq.com", "涂年生", "c");//抄送
MailObj.Priority = 1;//邮件的紧急程序,1 为最快,5 为最慢, 3 为默认值
MailObj.AddAttachment(@"C:\\DSCF0066.jpg", false, "image/jpg");
MailObj.AddAttachment(@"C:\\EmployeesInfo.xls", false, "application/excel");
MailObj.AddAttachment(@"C:\\geovindu.doc", false, "application/msword");//大附件发送 23.77M
Thread.Sleep(1000);
try
{
bool bool_OK = MailObj.Send("mail.jw28.com", false);
if (bool_OK == true)
{
MessageBox.Show("邮件发送成功!");
//this.label3.Text = "邮件发送成功!"; //不可以在线程中用
}
if (bool_OK == false)
{
MessageBox.Show("邮件发送失败!");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
MailObj.Close();
使用Net.Mail、CDO组件、JMail组件三种方式发送邮件
原文:使用Net.Mail、CDO组件、JMail组件三种方式发送邮件
一、使用Net.Mail
需要服务器认证,大部分服务器端口为25.
View Code
1 /// <summary> 2 /// 用MailMessage通过需要认证的SMTP服务器发送邮件,可以发送附件 3 /// </summary> 4 /// <param name="frmAddress">发件箱地址,例:myaccount@163.com</param> 5 /// <param name="password">发件箱登录密码</param> 6 /// <param name="toAddress">收件箱地址,多个地址使用";"隔开,例:youraccount@sina.com</param> 7 /// <param name="copyTo">抄送地址,多个地址使用";"隔开,例:hisaccount@QQ.com</param> 8 /// <param name="mailSubject">邮件主题,例:MailTest</param> 9 /// <param name="mailContent">邮件内容,例:Hello</param>10 /// <param name="mailserver">发件箱所在的SMTP服务器,例:smtp.163.com</param>11 public void NetSendMail(string frmAddress, string password, string toAddress, string copyTo, string mailSubject, string mailContent, string mailserver)12 {13 ///添加发件人地址14 MailMessage mailMsg = new MailMessage();15 mailMsg.From = new MailAddress(frmAddress);16 ///添加收件人地址17 string split = ";";18 string[] toList = toAddress.Trim().Split(split.ToCharArray());19 for (int i = 0; i < toList.Length; i++)20 {21 mailMsg.To.Add(toList[i].Trim());22 }23 24 ///添加抄送地址25 string[] ccList = copyTo.Trim().Split(split.ToCharArray());26 for (int i = 0; i < ccList.Length; i++)27 {28 if (ccList[i].Trim().Length > 0)29 {30 mailMsg.CC.Add(ccList[i].Trim());31 }32 }33 34 ///添加邮件主题35 mailMsg.Subject = mailSubject.Trim();36 mailMsg.SubjectEncoding = Encoding.UTF8;37 38 ///添加邮件内容39 mailMsg.Body = mailContent;40 mailMsg.BodyEncoding = Encoding.UTF8;41 mailMsg.IsBodyHtml = true; //正文是否为html样式42 43 ///添加邮件附件 44 HttpFileCollection fileList = HttpContext.Current.Request.Files;45 for (int i = 0; i < fileList.Count; i++)46 { ///添加单个附件47 HttpPostedFile file = fileList[i];48 if (file.FileName.Length <= 0 || file.ContentLength <= 0)49 {50 break;51 }52 string path = Server.MapPath("~/FileUpload/"); //附件保存在程序所在的目录FileUpload下53 string name = System.IO.Path.GetFileName(file.FileName);54 file.SaveAs(path + name);55 mailMsg.Attachments.Add(new System.Net.Mail.Attachment(file.FileName));56 }57 try58 {59 //实例化SmtpClient邮件发送类对象60 SmtpClient client = new SmtpClient(mailserver, 25); //大部分smtp服务器的端口是2561 //设置用于验证发件人身份的凭据62 client.Credentials = new System.Net.NetworkCredential(frmAddress, password);63 //发送邮件64 client.Send(mailMsg);65 Response.Write("<script type='text/javascript'>alert('发送成功!')</script>");66 }67 catch68 {69 Response.Write("<script type='text/javascript'>alert('发送失败')</script>");70 }71 }
二、使用CDO组件
View Code
1 /// <summary> 2 /// 用CDO组件通过需要认证的SMTP服务器发送邮件。 3 /// 添加cdosys.dll引用,可以在系统目录(如c:\winnt或c:\windows)的system32子目录中找到它(cdosys.dll)。 4 /// </summary> 5 /// <param name="frmAddress">发件箱地址,例:myaccount@163.com</param> 6 /// <param name="password">发件箱登录密码</param> 7 /// <param name="toAddress">收件箱地址,多个地址使用";"隔开,例:youraccount@sina.com</param> 8 /// <param name="copyTo">抄送地址,多个地址使用";"隔开,例:hisaccount@QQ.com</param> 9 /// <param name="mailSubject">邮件主题,例:MailTest</param>10 /// <param name="mailContent">邮件内容,例:Hello</param>11 /// <param name="mailserver">发件箱所在的SMTP服务器,例:smtp.163.com</param>12 public void CDOSendMail(string frmAddress, string password, string toAddress, string copyTo, string mailSubject, string mailContent, string mailserver)13 {14 try15 {16 CDO.Message oMsg = new CDO.Message();17 18 oMsg.From = frmAddress; //添加发件人19 20 oMsg.To = toAddress; //多人用“;”,“,”分开,自动识别,21 22 oMsg.CC = copyTo;23 oMsg.Subject = mailSubject;24 oMsg.HTMLBody = "<html><body>" + mailContent + "</body></html>";25 CDO.IConfiguration iConfg = oMsg.Configuration;26 ADODB.Fields oFields = iConfg.Fields;27 28 oFields["http://schemas.microsoft.com/cdo/configuration/sendusing"].Value = 2;29 oFields["http://schemas.microsoft.com/cdo/configuration/sendemailaddress"].Value = frmAddress;30 oFields["http://schemas.microsoft.com/cdo/configuration/smtpaccountname"].Value = toAddress;31 oFields["http://schemas.microsoft.com/cdo/configuration/sendusername"].Value = frmAddress;32 oFields["http://schemas.microsoft.com/cdo/configuration/sendpassword"].Value = password;33 oFields["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"].Value = 1;34 //value=0 代表Anonymous验证方式(不需要验证)35 //value=1 代表Basic验证方式(使用basic (clear-text) authentication. 36 //The configuration sendusername/sendpassword or postusername/postpassword fields are used to specify credentials.)37 //Value=2 代表NTLM验证方式(Secure Password Authentication in Microsoft Outlook Express)38 oFields["http://schemas.microsoft.com/cdo/configuration/languagecode"].Value = 0x0804;39 oFields["http://schemas.microsoft.com/cdo/configuration/smtpserver"].Value = mailserver;40 41 oFields.Update();42 oMsg.BodyPart.Charset = "gb2312";43 oMsg.HTMLBodyPart.Charset = "gb2312";44 45 46 //添加邮件附件 47 HttpFileCollection fileList = HttpContext.Current.Request.Files;48 for (int i = 0; i < fileList.Count; i++)49 { ///添加单个附件50 HttpPostedFile file = fileList[i];51 if (file.FileName.Length <= 0 || file.ContentLength <= 0)52 {53 break;54 }55 string path = Server.MapPath("~/FileUpload/"); //附件保存在程序所在的目录FileUpload下56 string name = System.IO.Path.GetFileName(file.FileName);57 file.SaveAs(path + name);58 oMsg.AddAttachment(file.FileName);59 }60 61 oMsg.Send();62 oMsg = null;63 }64 catch (Exception e)65 {66 throw e;67 }68 }
三、使用JMail组件
View Code
1 /// <summary> 2 /// 用JMail组件发送邮件。 3 /// 添加jmail.dll引用 4 /// </summary> 5 /// <param name="frmAddress">发件箱地址,例:myaccount@163.com</param> 6 /// <param name="password">发件箱登录密码</param> 7 /// <param name="toAddress">收件箱地址,多个地址使用";"隔开,例:youraccount@sina.com</param> 8 /// <param name="copyTo">抄送地址,多个地址使用";"隔开,例:hisaccount@QQ.com</param> 9 /// <param name="mailSubject">邮件主题,例:MailTest</param>10 /// <param name="mailContent">邮件内容,例:Hello</param>11 /// <param name="mailserver">发件箱所在的SMTP服务器,例:smtp.163.com</param>12 public bool JMailSendMail(string frmAddress, string password, string toAddress, string copyTo, string mailSubject, string mailContent, string mailserver)13 {14 try15 {16 MessageClass jmMessage = new MessageClass();17 jmMessage.Charset = "gb2312";18 jmMessage.ISOEncodeHeaders = false; //信头编码iso-8859-1字符集19 jmMessage.Encoding = "base64"; //附件的编码格式20 //jmMessage.ContentType = "text/html"; //正文类型,去掉,否则正文出现乱码21 22 jmMessage.MailServerUserName = frmAddress; //发件箱登录名23 jmMessage.MailServerPassWord = password; //发件箱密码24 25 jmMessage.From = frmAddress; //发件箱26 27 jmMessage.Subject = mailSubject;28 jmMessage.Body = mailContent;29 30 //回执,当对方阅读了邮件后提醒是否发送回执31 jmMessage.ReturnReceipt = true;32 jmMessage.AddNativeHeader("Disposition-Notification-To", frmAddress);//回执接受人的邮件地址33 34 //收件箱35 string split = ";";36 string[] toList = toAddress.Trim().Split(split.ToCharArray());37 for (int i = 0; i < toList.Length; i++)38 {39 jmMessage.AddRecipient(toList[i].Trim(), "", "");40 }41 42 //抄送43 string[] coList = copyTo.Trim().Split(split.ToCharArray());44 for (int i = 0; i < coList.Length; i++)45 {46 jmMessage.AddRecipientCC(coList[i].Trim(), "", "");47 }48 49 ///添加邮件附件 50 HttpFileCollection fileList = HttpContext.Current.Request.Files;51 for (int i = 0; i < fileList.Count; i++)52 { ///添加单个附件53 HttpPostedFile file = fileList[i];54 if (file.FileName.Length <= 0 || file.ContentLength <= 0)55 {56 break;57 }58 string path = Server.MapPath("~/FileUpload/"); //附件保存在程序所在的目录FileUpload下59 string name=System.IO.Path.GetFileName(file.FileName);60 file.SaveAs(path + name);61 jmMessage.AddAttachment(file.FileName);62 }63 64 if (jmMessage.Send(mailserver, false))65 {66 return true;67 }68 else69 {70 return false;71 }72 }73 catch (Exception)74 {75 76 throw;77 }78 }
对于JMail组件,通常我们遇到的错误是:'The message was undeliverable. All servers failed to receive the message ',这其实是JMAIL返回的错误,并不是ASP代码产生的,根本原因是MAIL SERVER拒绝了JMAIL的请求.
究其原因,是那些服务器不提供SMTP服务或者没有开启smtp服务;或是在服务器端开启了'禁止邮件中继服务'选项,也就是说不在其允许的IP段或指定范围内的空间里的程序是无法使用其SMTP服务的。解决方案:使用支持smtp的邮件服务器. 使用支持外来jmail申请验证身份,发送邮件的邮件服务器。 最好:使用自己的待遇smtp功能的企业邮局。因为外面的免费的邮局可能会有一些特殊设置,不如防止垃圾邮件,防止盗用邮件身份等等!
Jmail发送首先要通过邮件服务器验证。如果你的服务器不支持SMTP或者你的账号不能使用SMTP服务那么就无法发送。163以前的用户默认是开通POP和SMTP服务的,但新用户都不开通,需要付费才能使用。要想确定某一邮箱是否可以使用POP和SMTP,你可以用foxmail等邮件软件看能否收取该邮箱信件。
目前发现可以通过的stmp服务器有:smtp.qq.com、smtp.163.com,也就是说可以使用该类的邮箱给其他邮箱发送邮件。
postfix反垃圾邮件说明
原文地址:http://guailele.blog.51cto.com/1156442/780223
#1、postfix配置说明(强烈建议参看“postfix权威指南 第十一章 反垃圾邮件”)#fqdn格式:完全限定域名格式,即用点分隔开的包括域名和主机名的主机全名# smtpd related configsmtpd_recipient_restrictions = permit_mynetworks, #检测客户端是否来自mynetworks或者mynetworks_style的网络,是的话返回OK,否则返回DUNNO状态码。 permit_sasl_authenticated, #检测用户认证是否通过的,认证通过的返回状态OK,否则返回DUNNO状态码。 reject_non_fqdn_hostname, #HELO/EHLO时:客户端提供的主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。 reject_non_fqdn_sender, #MAIL FROM时:客户端提供的主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。 reject_non_fqdn_recipient, #RCPT TO时:客户端提供的主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。 reject_unauth_destination, #RCPT TO时:收件人不在postfix管辖的区域(由mydestination定义),返回REJECT,否则返回DUNNO状态码。 reject_unauth_pipelining, #禁止非授权客户端使用pipelining reject_invalid_hostname #HELO/EHLO时:客户端提供的主机名不是有效的主机名时,返回REJECT,否则返回DUNNO状态码。# SMTP sender login matching configsmtpd_sender_restrictions = permit_mynetworks, #检测客户端是否来自mynetworks或者mynetworks_style的网络,是的话返回OK,否则返回DUNNO状态码。 reject_sender_login_mismatch, #拒绝发送者在$smtpd_sender_owner_maps中所匹配的用户名和sasl登录名不一致的连接。 reject_authenticated_sender_login_mismatch, #拒绝认证成功的发送者在$smtpd_sender_owner_maps中所匹配的用户名和sasl登录名不一致的连接。 reject_unauthenticated_sender_login_mismatch #拒绝认证失败的发送者在$smtpd_sender_owner_maps中所匹配的用户名和sasl登录名不一致的连接。smtpd_sender_login_maps = mysql:/etc/postfix/mysql_virtual_sender_maps.cf, mysql:/etc/postfix/mysql_virtual_alias_maps.cf #2、smtp会话过程的各个限制条件参数:详看图片“stmp对话过程以及各个阶段对于的限制条件.png”,共分为7个顺序过程的参数限制:1、smtpd_client_restrictions2、smtpd_helo_restrictions3、smtpd_sender_restrictions4、smtpd_recipient_restrictions5、smtpd_data_restrictions6、header_checks7、body_checks#3、会话过程对比:#---------------------------------------------------------------------------root@mail:/etc/postfix# telnet mail.jmail.com 25Trying 192.168.0.234...Connected to mail.jmail.com.Escape character is '^]'.220 ESMTP #smtpd_client_restrictions#---------------------------------------------------------------------------ehlo mail.zmail.com #smtpd_helo_restrictions250-mail.jmail.com250-PIPELINING250-SIZE 5242880250-VRFY250-ETRN250-STARTTLS250-AUTH LOGIN PLAIN250-AUTH=LOGIN PLAIN250-ENHANCEDSTATUSCODES250-8BITMIME250 DSN#---------------------------------------------------------------------------MAIL FROM:<zyq@zmail.com> #smtpd_sender_restrictions250 2.1.0 Ok#---------------------------------------------------------------------------RCPT TO:<jj@extmail.org> #smtpd_recipient_restrictions250 2.1.5 Ok#---------------------------------------------------------------------------DATA #smtpd_data_restrictions354 End data with <CR><LF>.<CR><LF>#---------------------------------------------------------------------------Client: To: jingjing<jj@extmail.org> #header_checksFrom:<zyq@zmail.com>Subject:SMTP Test!#---------------------------------------------------------------------------this is a test message body! #body_checks.250 2.0.0 Ok: queued as 959FDE03CA#---------------------------------------------------------------------------#4、限制条件与对应的受检信息限制条件 客户端提供的受检信息#RBL:实时黑名单reject_rbl_client 客户端提供的IP地址或则主机名称 #smtpd_client_restrictionsreject_rhsbl_clientreject_unknown_clientcheck_client_access type:mapname helo提供的主机名称 #smtpd_helo_restrictions permit_naked_ip_addressreject_invalid_hostnamereject_non_fqdn_hosnamereject_unknown_hosnamecheck_helo_access type:mapname MAIL FROM提供的寄件人邮件地址 #smtpd_sender_restrictionsreject_non_fqdn_senderreject_rhsbl_senderreject_unknown_sender_domaincheck_sender_access type:mapname RCPT TO提供的收件人的地址 #smtpd_recipient_restrictionspermit_auth_destinationpermit_mx_backupreject_non_fqdn_recipientreject_unauth_destinationreject_unkownn_recipient_domaincheck_recipient_access type:mapname reject_unauth_pipeling DATA命令permint 无条件批准reject 无条件拒绝defer 无条件延迟warn_if_reject 将原本的REJECT动作改为WARN(eg:warn_if_reject reject_unauth_destination 注意此命令必须放在其他条件之前,不能单独调用)reject_unauth_pipelining 禁止非授权客户端使用pipelining#参数详细说明reject_rbl_client #拒绝来自属于rbl和rhsbl列表中的地址进行连接。通过检查一个ip地址或域名是否存在于domain.tld的rbl或rhsbl中,可以判断该客户端是否被列入了domain.tld的实时黑名单,从而决定是否接受连接。reject_rhsbl_client #拒绝来自属于rbl和rhsbl列表中的地址进行连接。通过检查一个ip地址或域名是否存在于domain.tld的rbl或rhsbl中,可以判断该客户端是否被列入了domain.tld的实时黑名单,从而决定是否接受连接。reject_unknown_client #拒绝客户的地址没有对应的dns的a记录或ptr记录的连接。通常有些机器,尤其是个人拨号用户的机器没有对应的a记录或ptr记录,所以要注意漫游用户的使用(漫游用户是指不在$mynetworks中,比如在别的isp拨号上网的用户。通常用smtp认证来解决这个问题)。check_client_access type:mapnamepermit_naked_ip_address #允许直接使用ip地址的连接。通常在helo/ehlo中使用主机名而不是ip地址。reject_invalid_hostname #拒绝无效格式的主机名的连接。 reject_non_fqdn_hosname #HELO/EHLO时:客户端提供的主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。 reject_unknown_hosname #HELO/EHLO时:客户端提供的主机名未知时,返回REJECT,否则返回DUNNO状态码。check_helo_access type:mapnamereject_non_fqdn_sender #MAIL FROM时:客户端提供的服务器主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。reject_rhsbl_sender #MAIL FROM时:拒绝来自实时黑名单的用户主机reject_unknown_sender_domain #MAIL FROM时:提供的网域无效时(DNS查询),返回REJECT,否则返回DUNNO状态码。check_sender_access type:mapnamepermit_auth_destination #RCPT TO时:#允许发往默认转发和默认接收的连接。#postfix默认转发以下的邮件:#来自$mynetworks中地址发送的邮件#发往$relay_domains中的域或其子域的邮件。但是不能包含邮件路由(如user@elsewhere@domain.tld)。#postfix默认接收最终投递目标符合如下条件的邮件:#目标在$inet_interfaces#目标在$mydestinations#目标在$virtual_alias_domains#目标在$virtual_mailbox_domainspermit_mx_backup #RCPT TO时:允许接收本地主机是邮件投递目标的mx地址的邮件。但是不能包含邮件路由(如user@elsewhere@domain.tld)reject_non_fqdn_recipient #RCPT TO时:客户端提供的服务器主机名不是RFC要求的完整形式(FQND),返回REJECT,否则返回DUNNO状态码。reject_unauth_destination #RCPT TO时:拒绝不是发往默认转发和默认接收的连接。(防止成为垃圾邮件服务器)。reject_unkownn_recipient_domain #RCPT TO时:拒绝发往未知域名的连接。check_recipient_access type:mapname reject_unauth_pipeling #DATA命令permint #无条件批准reject #无条件拒绝defer #无条件延迟warn_if_reject #将原本的REJECT动作改为WARN(eg:warn_if_reject reject_unauth_destination 注意此命令必须放在其他条件之前,不能单独调用)reject_unauth_pipelining #禁止非授权客户端使用pipelining
[C# 网络编程系列]专题十:实现简单的邮件收发器
引言:
在我们的平常工作中,邮件的发送和接收应该是我们经常要使用到的功能的。因此知道电子邮件的应用程序的原理也是非常有必要的,在这一个专题中将介绍电子邮件应用程序的原理、电子邮件应用程序中涉及的协议和实现一个简答的电子邮件收发器程序。
一、邮件应用程序基本知识
1.1 电子邮件原理及相关协议
说到电子邮件的原理,其实和我们现实生活中寄邮件和寄包裹是一样的原理的。就让我们先回顾下现实生活中寄邮件的流程吧——首先,我们先写好信,信封上面写好收信人的地址,写信人的地址,然后把信放到寄信箱中,然后邮局的人会某个时候去这个信箱中的信取出来,然后邮局的人根据信封上写的收信人地址进行转发到当地的邮局,当地邮局然后把信寄到收信人的信箱中(寄包裹的话可能会电话联系,像我们在淘宝,京东买的东西的,收货人就是通过电话联系一样),最后收信人会到自己的信箱中收取信件。上面大致是我们平时生活中寄信的一个流程的。前面已经讲过电子邮件的原理和这个差不多的,下面就介绍了本专题中电子邮件的原理,大家可以和现实生活中的寄信过程进行对比下的,这样可以更加容易理解和掌握:
我们通过电子邮件应用(例如 基于客户端的Outlook电子邮件软件 和一些基于Web的电子邮件系统——新浪邮箱、谷歌邮箱、QQ邮箱等都属于电子邮件应用)将一封写好的邮件(相当于现实生活中的信,当然邮件也要写明收件人地址,邮件内容等信息的)通过电子邮件协议(SMTP,在后面的电子邮件相关协议中会介绍)发送到SMTP服务器(就是存储邮件的地方,相当于生活中的邮局一样),然后SMTP服务器根据收件人的地址通过SMTP协议转发到相应SMTP接收服务器上,(SMTP服务器进行转发相当于现实生活中邮局的人配送信的过程,配送到收件人当地的邮局,然而现实生活中邮局都是一家,所以可以相互识别——意思就是发送到当地邮局,当地邮局会接收,并且帮助你发送到指定人的信箱中,在网上上就是通过SMTP协议来规定这样的一个过程的,发送到别人的SMTP服务器上别人的服务器必须要认识发送来的邮件并接收)结束,接收端邮件服务器(POP3服务器)把邮件存放到接受者的电子信箱内(相当于当地邮局的人把信放到收信人的邮箱中),最后收件人可以登录自己的电子信箱,再与POP3服务器进行连接,从POP3服务器上下载发送来的邮件,这样在收件人的电子信箱中就可以看到发送来的电子邮件了(这就是现实生活中收信人从自己的信箱中取信的一个过程)。
注:括号中都是个人的理解,如果有什么不对的地方还望大家指出来,我好及时更正。
上面已经把电子邮件的原理和现实生活中寄信的过程进行对比,相信大家可以更加清楚电子邮件的原理和发送接收过程的,其实网络上的很多应用都可以以现实生活的例子去理解,这样的话我认为可以加深对知识的理解。下面就介绍下电子邮件中的相关协议的内容:
网络上的应用的核心就是协议,因为协议让网络上的客户端相互认识发生来的数据,所以电子邮件应用也不例外,也有相关的电子邮件协议来完成发送电子邮件和接收电子邮件的过程,这些协议主要是:SMTP(简单邮件传输协议,Simple Mail Transfer Protocol)、POP3(邮局协议,Post Office Protocol)和IMAP(网络邮件访问协议,Internet Message Access Protocol)。
SMTP——SMTP 主要负责将邮件从一台机器转发至另一台机器(可以对照上面电子邮件的过程来理解SMTP的作用)
POP3——3表示POP协议的版本,主要负责将邮件从邮箱中(POP3服务器)传输到本地计算机。
IMAP——现在常用的版本为第四版本,即IMAP4,主要负责邮件的检索和处理功能,客户端不需要下载邮件到本地计算机,可直接从邮件客户端软件对服务器上的信件和文 件目录进行操作,它是POP3的替代协议的。
1.2 邮件系统的分类
邮件系统主要分为两类的——基于客户端的邮件系统和基于Web浏览器的邮件系统。Office OutLook就是基于客户端的邮件客户端系统,而像我们经常使用的QQ邮箱、新浪、网易邮箱等都是属于基于Web浏览器的邮件系统,基于客户端的邮件系统的收发过程,通过下面的图片来描述(图片从网上摘下的):
图 1.1 基于客户端的邮件收发过程
发送方通过邮件客户端,将编辑好的邮件向邮件服务(SMTP服务器,在发送过程中也叫发送端邮件服务器)发送,发送端邮件服务器根据收件人的地址来识别接收端邮件服务器(POP3服务器),然后向POP3服务器发送邮件信息,接收端邮件服务器将邮件存放在接收者的电子信箱中,并告知接收者有新邮件,接收者通过邮件客户端与POP3服务器连接后,就可以查看新邮件。
然而,基于Web浏览器的邮件系统与基于客户端的邮件系统不同的地方有:
基于Web浏览器邮件系统用户代理(代理的概念也就是用户不是直接与服务器进行通信,而是通过代理的方式,让代理去与服务器通信,然后用户在从代理中获的服务器的信息,代理也就是中间人的作用,相当于生活中中介,在.net中很多技术都用到了代理,例如委托的概念其实也就是代理的一个概念的)是Web浏览器,基于客户端的邮件系统而是邮件客户端应用程序,一般是Windows Form程序。
浏览器发送邮件到SMTP服务器和从POP3服务器中获得邮件的方式都是通过HTTP协议来实现,与基于客户端的邮件系统不同(基于客户端的邮件系统发送通过SMTP协议或ESMTP(Extended SMTP),获得通过POP3或IMAP协议)。
1.3 目前主要的电子邮件服务系统
电子邮件服务系统——就是向大家提供邮箱服务的服务系统,这样的系统当然是由专门的公司进行研发的,我们一般叫这样的公司为邮件服务商,我们平常使用的网易邮箱,新、Gmail邮箱等都是建立在电子邮件服务系统(这里我的理解是——我们使用的新浪,网易等邮箱相当于现实生活中每个人的信箱,通过信箱可以获得邮局来的信,同样道理通过邮箱可以获得邮件服务系统的邮件,这样电子邮件系统相当于邮局) 。现在主要电子邮件服务系统主要有下面几种:
基于Postfix/Qmail的邮件系统。例如,雅虎邮箱基于Qmail系统
微软Exchange 邮件系统
IBM Lotus Domino邮件系统
Scalix邮件系统
Zimbra邮件系统
MDeamon邮件系统
二、.Net 平台对邮件发送功能的支持
在.NET类库中,在System.Net.Mail命名空间下定义了对邮件处理的类,这样使邮件的发送更加方便(这些类也就是对SMTP协议的封装,使我们更好地区编程,只需要使用类中的方法和属性等去完成邮件的发送,避免写复杂的SMTP协议的命令),下面是一张在System.Net.Mail命名空间下对邮件发送的支持的类截图:
从图片中类的名字中也可以看出每个类的作用的,在这里我就不一个介绍的, 大家可以参考MSDN去看每个类的使用,并且我在后面程序的实现部分也会有详细的注释去介绍程序中使用到类的使用。从图中还可以i看出一点——就是只有SMTP的字样,却没有POP3这样的字样的,这说明.Net类库本身中并没有提供对POP3协议的封装类,但是我们可以使用Jmail组件来完成从POP3服务器中收取邮件的功能,具体的使用将在后面的邮件收发器程序中邮件的接收部分介绍的。
三、邮件收发器程序的实现
3.1 邮件发送功能的实现
3.1.1 SMTP协议
SMTP 协议是用于电子邮件的传输的协议,电子邮件是通过SMTP服务器进行发送的,SMTP服务器的默认端口为25,通常发送邮件有两种方式——一种是不使用客户端认证,即客户端可以使用匿名发送邮件(这种方式叫做SMTP);另一种是客户端必须提供用户名和密码认证(这种方式叫做ESMTP,Extended SMTP)目前大部分邮件服务器采用用户名和密码认证的方式。
客户端发送邮件过程为——先通过客户端软件(本程序中的邮件收发器)将邮件发送到SMTP服务器,然后再由SMTP服务器发送到目标SMTP服务器。下面介绍SMTP协议的内容:
SMTP协议总共定义了14个命令,命令由命令码和气候的参数域组成, 不区别大小写的(通过前面专题的讲述可以得出各个协议的命令组成都差不多的),下面就简单介绍下5个常用的命令码
名称
解释
HELO或EHLO
发送连接到服务器的命令,EHLO主要用于与ESMTP服务器建立连接时发送的命令
MAIL FROM
指定发件人的邮件地址
Rcpt to
指定收件人的邮件地址
Data
指定邮件正文内容,邮件内容以单独一行 ”.” 表示接触
Quit
关闭与服务器的连接,然后退出
电子邮件由信封、首部、正文和结束符号4部分组成,下面就具体介绍下这4个部分的内容:
1. 信封
信封包括发信人的邮件地址和接收人的邮件地址,具体对应两条SMTP命令——Mail from: mytest1989@sina.cn(发信人的地址)和Rcpt to: 794170314@qq.com
2. 首部
首部中常用的命令有:
Subject:<邮件主题>——表示邮件的主题
Date:<时间>——表示发邮件的时间
reply-to:<邮件地址>——表示邮件的回复地址
Content-Type:<邮件类型>——表示邮件包含文本、HTML超文本和附件的类型。
X-Priority:<邮件优先级>——表示邮件发送的优先级,优先级为3表示为普通邮件;如 X-Priority:3
3. 正文
正文当然指的就是邮件的内容了, 用Data命令指定,首部以一个空行结束,下面就是正文部分
4. 结束符号
邮件以“."结束,
接收方收到SMTP命令之后,会给出一个响应码,每个命令都只有一个响应码,SMTP响应码也是由3位数字组成,后面附加一些文本信息,响应信息的格式为:
响应码<空格>文本信息<回车换行>
客户端发出一条命令后,服务器端返回一个响应,发送者在发送下一条命令前必须等待服务器的响应,成功接收到响应码后才继续发送命令。
附:SMTP常用的响应码:
响应码
解释
响应码
解释
211
系统状态或系统帮助响应
421
服务未就绪,关闭了传输通道
214
帮助信息
501
参数格式错误
220
服务就绪
502
命令不可实现
221
服务关闭传输通道
535
用户验证失败
235
用户验证成功
553
邮箱名不可用,要求的操作未执行
334
等待用户输入验证
554
操作失败
354
开始邮件输入
3.1.2 邮件的发送过程
第一步:客户端与服务器建立连接(该步中客户端首先发送EHLO local 连接命令,服务器如果返回“220”响应码表示服务器准备就绪了,客户端再继续发送“Auto login”命令,请求登录,服务器收到命令后返回“334”响应码,表示要输入用户名,之后客户端发送用户名命令,等到响应后再发送密码命令,具体在程序的实现中也会有注释。)
第二步:客户端发送邮件的信封
第三步:开始发送邮件数据,(包括邮件首部,正文和结束符号,注:结束符号要单独占一行,表示邮件发送结束)
第四步: 客户端与服务器断开连接。
3.1.3 发送功能的实现代码
相信有了上面的理论解释邮件发送的过程后,实现邮件发送的功能并不难的,并且.net类库中SMTPClient类帮我们封装了SMTP协议,使得我们实现邮件发送功能就不要记住那些具体的命令了, 只需要使用该类中提供的方法来完成邮件的发送(当然你也可以通过发送命令的方式实现,SMTPClient类的方法也是帮我们完成发送命令功能而已的),下面是邮件发送功能的核心代码:
View Code
1 #region 邮件发送功能代码
2 // 添加附件
3 private void btnAddFile_Click(object sender, EventArgs e)
4 {
5 OpenFileDialog openFileDialog = new OpenFileDialog();
6 openFileDialog.CheckFileExists = true;
7 // 只接受有效的文件名
8 openFileDialog.ValidateNames = true;
9 // 允许一次选择多个文件作为附件
10 openFileDialog.Multiselect = true;
11 openFileDialog.Filter = "所有文件(*.*)|*.*";
12 if (openFileDialog.ShowDialog() != DialogResult.OK)
13 {
14 return;
15 }
16 if (openFileDialog.FileNames.Length > 0)
17 {
18 // 因为这里允许选择多个文件,所以这里用AddRange而没有用Add方法
19 cmbAttachment.Items.AddRange(openFileDialog.FileNames);
20 }
21 }
22
23 // 删除附件
24 private void btnDeleteFile_Click(object sender, EventArgs e)
25 {
26 int index = cmbAttachment.SelectedIndex;
27 if (index == -1)
28 {
29 MessageBox.Show("请选择要删除的附件!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
30 return;
31 }
32 else
33 {
34 cmbAttachment.Items.RemoveAt(index);
35 }
36 }
37
38 // 发送邮件
39 private void btnSend_Click(object sender, EventArgs e)
40 {
41 this.Cursor = Cursors.WaitCursor;
42 // 实例化一个发送的邮件
43 // 相当于与现实生活中先写信,程序中把信(邮件)抽象为邮件类了
44 MailMessage mailMessage = new MailMessage();
45 // 指明邮件发送的地址,主题,内容等信息
46 // 发信人的地址为登录收发器的地址,这个收发器相当于我们平时Web版的邮箱或者是OutLook中配置的邮箱
47 mailMessage.From = new MailAddress(tbxUserMail.Text);
48 mailMessage.To.Add(txbSendTo.Text);
49 mailMessage.Subject = txbSubject.Text;
50 mailMessage.SubjectEncoding = Encoding.Default;
51 mailMessage.Body = richtbxBody.Text;
52 mailMessage.BodyEncoding = Encoding.Default;
53 // 设置邮件正文不是Html格式的内容
54 mailMessage.IsBodyHtml = false;
55 // 设置邮件的优先级为普通优先级
56 mailMessage.Priority = MailPriority.Normal;
57 //mailMessage.ReplyTo = new MailAddress(tbxUserMail.Text);
58
59 // 封装发送的附件
60 System.Net.Mail.Attachment attachment = null;
61 if (cmbAttachment.Items.Count > 0)
62 {
63 for (int i = 0; i < cmbAttachment.Items.Count; i++)
64 {
65 string fileNamePath = cmbAttachment.Items[i].ToString();
66 string extName = Path.GetExtension(fileNamePath).ToLower();
67 if (extName == ".rar" || extName == ".zip")
68 {
69 attachment = new System.Net.Mail.Attachment(fileNamePath, MediaTypeNames.Application.Zip);
70 }
71 else
72 {
73 attachment = new System.Net.Mail.Attachment(fileNamePath,MediaTypeNames.Application.Octet);
74 }
75
76 // 表示MIMEContent-Disposition标头信息
77 // 对于ContentDisposition具体类的解释大家可以参考MSDN
78 // 这里我就不重复贴出来了,给个地址: http://msdn.microsoft.com/zh-cn/library/System.Net.Mime.ContentDisposition.aspx (着重看备注部分)
79 ContentDisposition cd = attachment.ContentDisposition;
80 cd.CreationDate = File.GetCreationTime(fileNamePath);
81 cd.ModificationDate = File.GetLastWriteTime(fileNamePath);
82 cd.ReadDate = File.GetLastAccessTime(fileNamePath);
83 // 把附件对象加入到邮件附件集合中
84 mailMessage.Attachments.Add(attachment);
85 }
86 }
87
88 // 发送写好的邮件
89 try
90 {
91 // SmtpClient类用于将邮件发送到SMTP服务器
92 // 该类封装了SMTP协议的实现,
93 // 通过该类可以简化发送邮件的过程,只需要调用该类的Send方法就可以发送邮件到SMTP服务器了。
94 smtpClient.Send(mailMessage);
95 MessageBox.Show("邮件发送成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
96
97 }
98 catch(SmtpException smtpError)
99 {
100 MessageBox.Show("邮件发送失败:[" + smtpError.StatusCode + "];["
101 + smtpError.Message+"];\r\n["+smtpError.StackTrace+"]."
102 ,"错误",MessageBoxButtons.RetryCancel,MessageBoxIcon.Error);
103 }
104 finally
105 {
106 mailMessage.Dispose();
107 this.Cursor = Cursors.Default;
108 }
109 }
110
111 #endregion
3.2 邮件接收功能的实现
3.2.1 POP3协议
前面介绍了邮件的发送,当然接收者需要登录邮箱来查看收到的邮件了,此时就必有有一个协议去读取服务器上邮件,POP3就是这样的一个协议。还有两外一种协议也是用来接收邮件的——IMAP协议,它与POP3协议区别有:1. IMAP使用的端口号是143而POP3邮件服务器通过监听110端口来提供POP3服务;
2 . IMAP 允许客户端在邮件服务器上建立文件夹来保持邮件,而不用把邮件下载到本地。而POP3需要把邮件下载到本地。
和SMTP协议一样,客户端要通过POP3协议从POP3服务器上获取邮件,也需要先与POP3服务器建立TCP连接,等待服务器向客户端发送确认信息表明连接成功时,客户端才可以继续发送命令给服务器来获取邮件,在POP3协议中,规定的命令也是几十条的,每条命令由命令和参数两部分组成,都是以回车换行结束,并且命令和参数之间由空格分隔,命令通常也是由3-4个字母组成,参数最多可以为40个字符的长度,而服务器的响应信息是由一个状态码和可能附加信息的字符组成,所有的响应信息也是以回车换行结束的。状态码和其他协议定义的状态码有点不一样,POP3服务器响应的状态码有两种——“+OK”(确定)和"-ERR"(失败)。这样客户端可以通过检查响应的状态码所包含的字符来判断服务器是否响应客户端发送的命令,即响应信息中包含“+OK”表示成功响应,包含“-ERR”表示服务器未响应。同时在程序的实现中大家可以通过Debug来查看响应消息的组成,这样可以加深理解。
3.2.2 邮件接收的过程
客户端从服务器接收邮件的过程主要经历3个状态:授权状态、操作状态和更新状态
(1)授权状态——客户端发送与POP3服务器的TCP连接请求,服务器接收后发送一个响应确认信息之后,此时客户端需要发送正确的用户名和密码进行确认,因为在邮件服务器上有很多用户邮箱,只有提供正确的用户名和密码才有权限访问自己的邮箱,就像现实生活中我们邮箱的钥匙一样的。
发送用户名命令: USER mytest1989@sina.cn
发送密码命令: PASS ******(这两个命令都在代码中有给出的,大家可以参考代码来理解邮件的接收过程)
(2) 操作状态——如果客户端提供了正确的用户名和密码,则授权状态也就通过了,就相当于打开了在服务器上自己的邮箱,现在用户就有权限进去下载,查看和删除邮件等操作的,然后在现实生活中的取邮件和删除邮件都很简单(只要打开了邮箱门,用手去拿就可以了),然后在网络应用上,这些操作都需要发送POP3命令给服务器,服务器接收到命令后再给出响应。操作中常用的命令有:
STAT 命令——该命令从服务器中获取邮件总数和总字节数,服务器响应命令返回邮件总数和总字节数
如:
?
客户端发送POP3命令: STAT
服务器响应命令: +OK 2 1340<BR>服务器响应命令:
LIST 命令——该命令从服务器中获得邮件列表和大小,服务器响应命令返回列出邮件列表和大小。
如:
?
客户端发送POP3命令:LIST
服务器响应命令: +OK 2 message(1430 octect)
服务器响应命令:1 700
服务器响应命令:2 730
服务器响应命令:<一个空行>
RETR 命令—— 该命令从服务器中获得一个邮件,格式为 RETR <邮件编号>
如:
?
客户端发送POP3命令:RETR 1
服务器响应命令: 700 octets
服务器响应命令:<邮件头和内容>
服务器响应命令: <空行>
DELETE 命令——该命令告诉服务器将邮件标记为删除。(此时只是逻辑删除)
(3)更新状态——客户端发送QUIT命令后,此时就进入更新状态,POP3服务器释放在操作状态中取得的资源,并将逻辑删除的邮件进行物理删除,然后关闭与客户端的TCP连接。这样整个邮件处理的过程就结束了。
3.2.3 接收功能的实现代码
有了前面接收邮件过程的介绍,再参考代码的实现,相信大家可以更好的理解客户端从POP3服务器中获取邮件的过程的,由于.net类库并没有帮我们封装POP3协议的实现类,要实现邮件的获取可以采用发送命令的方式,也可以使用Jmail组件,这个组件其实就是POP3协议的封装类,既然微软没有帮我们做,其他公司帮我们做好后来帮助我们简单的实现邮件的接收的一个类库罢了。然后在使用这个组件的过程中出现了好几个问题的,在源码中我都解释,大家可以下载源代码后查看的。
实现邮件接收的核心代码如下:
View Code
// 登录邮箱(这里是本程序——邮件收发器)
private void btnLogin_Click_1(object sender, EventArgs e)
{
// 与POP3服务器建立TCP连接
// 建立连接后把服务器上的邮件下载到本地
// 设置当前界面的光标为等待光标(就是我们看到的一个动的圆形)
Cursor.Current = Cursors.WaitCursor;
lsttbxStatus.Items.Clear();
try
{
// POP3服务器通过监听TCP110端口来提供POP3服务的
// 向POP3服务器发出tcp请求
tcpClient = new TcpClient(tbxPOP3Server.Text, 110);
lsttbxStatus.Items.Add("正在连接...");
}
catch
{
MessageBox.Show("连接失败", "错误", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
lsttbxStatus.Items.Add("连接失败!");
return;
}
// 连接成功的情况
networkStream = tcpClient.GetStream();
streamReader = new StreamReader(networkStream, Encoding.Default);
streamWriter = new StreamWriter(networkStream, Encoding.Default);
streamWriter.AutoFlush = true;
string str;
// 读取服务器返回的响应连接信息
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("服务器拒接了连接请求");
return;
}
// 如果服务器接收请求
// 向服务器发送凭证——用户名和密码
// 向服务器发送用户名,请求确认
lsttbxStatus.Items.Add("核实用户名阶段...");
SendToServer("USER " + tbxUserMail.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("用户名错误.");
return;
}
// 用户名审核通过后再发送密码等待确认
// 向服务器发送密码,请求确认
SendToServer("PASS "+txbPassword.Text);
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("密码错误!");
return;
}
lsttbxStatus.Items.Add("身份验证成功,可以开始会话");
// 向服务器发送LIST 命令,请求获得邮件列表和大小
lsttbxStatus.Items.Add("获取邮件....");
SendToServer("LIST");
str = GetResponse();
if (CheckResponse(str) == false)
{
lsttbxStatus.Items.Add("获取邮件列表失败");
return;
}
lsttbxStatus.Items.Add("邮件获取成功");
// 窗口控件控制
tabControlMyMailbox.Enabled = true;
btnReadMail.Enabled = false;
btnDownLoad.Enabled = false;
btnDeleteMail.Enabled = false;
// 登陆成功后实例化邮件发送对象,以便后面完成发送邮件的操作
// 实例化邮件发送类(SmtpClient)对象
if (smtpClient == null)
{
smtpClient = new SmtpClient();
smtpClient.Host = tbxSmtpServer.Text;
smtpClient.Port = 25;
// 不使用默认凭证,即需要认证登陆
smtpClient.UseDefaultCredentials = false;
smtpClient.Credentials = new NetworkCredential(tbxUserMail.Text, txbPassword.Text);
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
}
// 登陆成功后,自动接收新邮件
// 开始接收邮件
try
{
btnRefreshMailList.PerformClick();
}
catch
{
MessageBox.Show("读取邮件列表失败!", "错误", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
}
lsttbxStatus.Items.Add("登陆成功!");
lsttbxStatus.TopIndex = lsttbxStatus.Items.Count - 1;
Cursor.Current = Cursors.Default;
// 窗口控件控制
richtbxMailContentReview.Enabled = true;
tbxUserMail.Enabled = false;
txbPassword.Enabled = false;
btnLogin.Enabled = false;
btnLogout.Enabled = true;
tbxSmtpServer.Enabled = false;
tbxPOP3Server.Enabled = false;
btnReadMail.Enabled = true;
btnDownLoad.Enabled = true;
btnDeleteMail.Enabled = true;
tabControlMyMailbox.Focus();
}
#region 处理与POP3服务器交互事件
// 获取服务器响应的信息
private string GetResponse()
{
string str = null;
try
{
str = streamReader.ReadLine();
if (str == null)
{
lsttbxStatus.Items.Add("连接失败——服务器没有响应");
}
else
{
lsttbxStatus.Items.Add("收到:[" + str + "]");
if (str.StartsWith("-ERR"))
{
str = null;
}
}
}
catch(Exception err)
{
lsttbxStatus.Items.Add("连接失败:[" + err.Message + "]");
}
return str;
}
// 检查响应信息
private bool CheckResponse(string responseString)
{
if (responseString == null)
{
return false;
}
else
{
if (responseString.StartsWith("+OK"))
{
return true;
}
else
{
return false;
}
}
}
// 向服务器发送命令
private bool SendToServer(string str)
{
try
{
// 这里必须使用WriteLine方法的,因为POP3协议中定义的命令是以回车换行结束的
// 如果客户端发送的命令没有以回车换行结束,POP3服务器就不能识别,也就不能响应客户端的请求了
// 如果想用Write方法,则str输入的参数字符中必须包含“\r\n”,也就是回车换行字符串。
streamWriter.WriteLine(str);
streamWriter.Flush();
lsttbxStatus.Items.Add("发送:[" + str + "]");
return true;
}
catch(Exception ex)
{
lsttbxStatus.Items.Add("发送失败:[" + ex.Message + "]");
return false;
}
}
#endregion
3.3 程序运行结果演示
首先输入邮箱名和密码登录到POP3服务器来获取邮件列表的演示:
然后在邮件列表中选中一个邮件进行阅读,然后进行回复邮件的操作演示(邮件的发送都可以附加附件发送出去):
阅读邮件的界面:
回复邮件的界面:
同时点击发送按钮后,就可以把邮件发送到sina的SMTP服务器上,再由新浪的SMTP服务器转发到QQ的SMTP服务器,QQ的POP3服务器中QQ的SMTP服务器获取收到的邮件,当QQ用户输入正确的邮箱名和密码后就可以从QQ的POP3服务器上获取收到的邮件。
点击发送按钮后成功发送邮件的图片:
四、总结
介绍到这里,本专题的内容就已经介绍完了,希望通过本专题可以让大家明白邮件发送和接收的原理,并且可以自定义一个简单邮件收发器的功能的,在后面一专题将介绍FTP协议(文件传输协议),并实现一个简单的文件上传和下载的程序。
附件:http://down.51cto.com/data/2361665
本文转自LearningHard 51CTO博客,原文链接:http://blog.51cto.com/learninghard/1038691,如需转载请自行联系原作者
ASP.NET中进行消息处理(MSMQ) 二
在我上一篇文章《ASP.NET中进行消息处理(MSMQ)一》里对MSMQ做了个通俗的介绍,最后以发送普通文本消息和复杂的对象消息为例介绍了消息队列的使用。 本文在此基础上继续介绍MSMQ的相关知识点,最后还是通过一个示例程序来分析MSMQ在实际项目开发中的应用。
建议:如果你对MSMQ不够了解,在你阅读本文前请先阅读第一部分:《ASP.NET中进行消息处理(MSMQ)一》。一、消息传递的优先级
在MSMQ中消息在队列里传输是分有优先级的,这里我就以实例的形式介绍下关于优先级的使用,优先级一共有七种,MessagePriority枚举里全部进行了封装。因这里只作程序演示就不一一列举出,仅用了Highest和Normal两种类型,关于消息队列上进行消息传输的七种优先级大家可以参考我下面提供的MessagePriority枚举源代码定义。
那么在发送消息的时候怎么指定消息的优先级呢?在Message对象里封装有一个属性Priority,接受一个枚举MessagePriority类型的值来设置消息传输的优先级。如下:
1System.Messaging.Message message = new System.Messaging.Message();
2message.Priority = MessagePriority.Highest; //最高消息优先级
下面来看看一个在消息传输中使用优先级的示例程序,通过示例程序会学习得更明白。示例程序界面:
根据界面可知,提供了消息名字,消息优先级和消息内容三个输入项,前面曾经说过,这里为了方便演示就仅用了Highest和Normal两种类型,当点击发送消息的时候就通过是否选择优先级来设置消息的优先级,代码如下:
1private void btnSend_Click(object sender, EventArgs e)
2{
3 //连接到本地的专用队列myQueue
4 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
5 System.Messaging.Message message = new System.Messaging.Message();
6 message.Label = tbName.Text;
7 message.Body = tbContext.Text;
8
9 if (cbPriority.Checked)
10 {
11 message.Priority = MessagePriority.Highest;
12 }
13 else
14 {
15 message.Priority = MessagePriority.Normal;
16 }
17 myQueue.Send(message);
18 MessageBox.Show("成功发送消息到队列");
19}
这里我们可以向队列里发送两条消息,以便后面测试读取方法,发送两条消息到队列,此时,从队列消息中可以看到:
"刷新队列"实质上就是把队列里的消息全部读取出来,具体的实现在《ASP.NET中进行消息处理(MSMQ)一》里已经作了详细的介绍,这里就不在多说,看看下面的代码:
1private void DisplayMessage()
2 {
3 //连接到本地队列
4 MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
5 myQueue.MessageReadPropertyFilter.Priority = true;
6 DataTable messageTable = new DataTable();
7 messageTable.Columns.Add("名字");
8 messageTable.Columns.Add("消息内容");
9 messageTable.Columns.Add("优先级");
10 XmlMessageFormatter formatter = new XmlMessageFormatter(new string[] { "System.String" });
11 try
12 {
13 //从队列中接收消息
14 System.Messaging.Message[] messages = myQueue.GetAllMessages();
15 for (int index = 0; index < messages.Length; index++)
16 {
17 messages[index].Formatter = formatter;
18
19 string label = messages[index].Label;
20 string body = messages[index].Body.ToString();
21 string priority = messages[index].Priority.ToString();
22
23 messageTable.Rows.Add(new string[] { label, body, priority });
24 }
25 this.dgvMessage.DataSource = messageTable;
26 }
27 catch (MessageQueueException e1)
28 {
29 MessageBox.Show(e1.Message);
30 }
31 }
32 }
这里封装了一方法,专门负责从队列里读取全部消息并绑定在DataGridView控件上,这里我们只需要在按扭Click事件里调用这方法就OK。
1private void btnRec_Click(object sender, EventArgs e)
2{
3 DisplayMessage();
4}
这就完成了给消息设置优先级的消息传输的应用,最终的测试结果如下:
注:要完成以上应用还需注意一点,由于消息的优先级是枚举类型,在直接messages[index].Priority.ToString();这种方式来获取优先级转化到字符串的时候,他需要一个过滤器(Filter),否则会抛出一个InvalidCastExceptionle类型的异常,异常信息"接收消息时未检索到属性 Priority。请确保正确设置了 PropertyFilter。",要解决这问题只需要把消息对象的MessageReadPropertyFilter(过滤器) 的Priority设置为true就OK了。见上面代码里!^.^
MessagePriority枚举源代码定义详细如下:
1// 摘要:
2// 指定消息队列在消息传递到队列的过程中应用于该消息的优先级,以及指定何时将消息插入目标队列。
3public enum MessagePriority
4{
5 // 摘要:
6 // 最低消息优先级。
7 Lowest = 0,
8 //
9 // 摘要:
10 // 位于 Low 和 Lowest 消息优先级之间。
11 VeryLow = 1,
12 //
13 // 摘要:
14 // 低消息优先级。
15 Low = 2,
16 //
17 // 摘要:
18 // 普通消息优先级。
19 Normal = 3,
20 //
21 // 摘要:
22 // 位于 System.Messaging.MessagePriority.High 和 System.Messaging.MessagePriority.Normal
23 // 消息优先级之间。
24 AboveNormal = 4,
25 //
26 // 摘要:
27 // 高消息优先级。
28 High = 5,
29 //
30 // 摘要:
31 // 位于 Highest 和 High 消息优先级之间。
32 VeryHigh = 6,
33 //
34 // 摘要:
35 // 最高消息优先级。
36 Highest = 7,
37}
二、事务性消息处理
事务我想大家对这个词应该都不会陌生,在操作数据库的时候经常都会用到事务,确保操作成功,要么全部完成(成功),要么全部不完成(失败)。在MSMQ中利用事务性处理,可以确保事务中的消息按照顺序传送,只传送一次,并且从目的队列成功地被检索。
那么,在MSMQ上使用事务性处理怎么实现呢?可以通过创建MessageQueueTransation类的实例并将其关联到MessageQueue组件的实例来执行,执行事务的Begin方法,并将其实例传递到收发方法。然后,调用Commit以将事务的更改保存到目的队列。
创建事务性消息和普通的消息有一点小小的区别,大家可从下图上体会到:
通过代码方式建立事务性消息队列只在Create方法的参数上动动手脚就OK,详细如下:
1//创建普通的专用消息队列
2MessageQueue myMessage = MessageQueue.Create(@".\private$\myQueue");
3//创建事务性的专用消息队列
4MessageQueue myTranMessage =MessageQueue.Create(@".\private$\myQueueTrans", true);
启动了事务,那么在发送和接收消息的时候肯定是与原来有一定的差别的,这里我就不做详细介绍,下面给出示意性代码,有兴趣的朋友可以直接下载本文示例程序代码了解更多。
普通的消息发送示意性代码:
1//连接到本地的队列
2MessageQueue myQueue = new MessageQueue(".\\private$\\myQueue");
3Message myMessage = new Message();
4myMessage.Body = "消息内容";
5myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
6//发送消息到队列中
7myQueue.Send(myMessage);
启动了事务后的消息发送示意性代码:
1//连接到本地的队列
2MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");
3
4Message myMessage = new Message();
5myMessage.Body = "消息内容";
6myMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
7
8MessageQueueTransaction myTransaction = new MessageQueueTransaction();
9//启动事务
10myTransaction.Begin();
11//发送消息到队列中
12myQueue.Send(myMessage, myTransaction); //加了事务
13//提交事务
14myTransaction.Commit();
15Console.WriteLine("消息发送成功!");
读取消息示意性代码:
1//连接到本地队列
2MessageQueue myQueue = new MessageQueue(".\\private$\\myQueueTrans");
3myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
4if (myQueue.Transactional)
5{
6 MessageQueueTransaction myTransaction = new MessageQueueTransaction();
7 //启动事务
8 myTransaction.Begin();
9 //从队列中接收消息
10 Message myMessage = myQueue.Receive(myTransaction);
11 string context = myMessage.Body as string; //获取消息的内容
12 myTransaction.Commit();
13 Console.WriteLine("消息内容为:" + context);
14}
三、异步消息处理 在MSMQ中对消息的操作分有同步化操作和异步化操作两种,那两者到底有何区别呢?简单的说同步化操作就是一项操作没有完成前它将堵塞整个进程直到操作完成,下一项操作才会执行。所谓异步化操作则相反,不会堵塞启动操作的调用线程。如果你想在检索消息但不想堵塞其他程序的执行,则可使用异步消息处理。
在MSMQ中异步接收消息使用BeginReceive方法和EndReceive方法来标记操作的开始和结束,异步查看消息则使用BeginPeek和EndPeek两个方法来标记异步读取的开始和结束。同时,异步接收和查看消息还可以指定超时时间,关于这点我在后续文章里再做详细的介绍,有兴趣的朋友可以关注。
这里我将使用《ASP.NET中进行消息处理(MSMQ)一》一文里使用过的Book类来作为消息传输,没阅读过的朋友请先阅读这篇文章,了解Book类的结构。下面提供了一个示例,创建队列和发送消息到队列在前面我们已经使用多次了这里就不贴代码了,发送消息这里与第一篇文章中介绍如何发送一个复杂的类型信息到队列比,只是多了事务处理,详细如下:
1/// <summary>
2/// 发送消息到队列
3/// </summary>
4private static void SendMessage()
5{
6 MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
7 if (myQueue.Transactional)
8 {
9 Book book = new Book();
10 book.BookId = 1001;
11 book.BookName = "ASP.NET";
12 book.BookAuthor = "ZhangSan";
13 book.BookPrice = 88.88;
14 Message message = new Message(book);
15 message.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.Async.Book) });
16
17 MessageQueueTransaction myTransaction = new MessageQueueTransaction();
18 myTransaction.Begin();
19 myQueue.Send(message, myTransaction);
20 myTransaction.Commit();
21 Console.WriteLine("消息成功发送到队列!");
22 }
23}
异步接收消息:
1/// <summary>
2/// 异步接收消息
3/// </summary>
4private static void AsyncReceiveMessage()
5{
6 MessageQueue myQueue = new MessageQueue(".\\private$\\myAsyncQueue");
7 if (myQueue.Transactional)
8 {
9 MessageQueueTransaction myTransaction = new MessageQueueTransaction();
10 //这里使用了委托,当接收消息完成的时候就执行MyReceiveCompleted方法
11 myQueue.ReceiveCompleted += new ReceiveCompletedEventHandler(MyReceiveCompleted);
12 myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQ.Async.Book) });
13 myTransaction.Begin();
14 myQueue.BeginReceive();//启动一个没有超时时限的异步操作
15 signal.WaitOne();
16 myTransaction.Commit();
17 }
18}
1private static void MyReceiveCompleted(Object source, ReceiveCompletedEventArgs asyncResult)
2{
3 try
4 {
5 MessageQueue myQueue = (MessageQueue)source;
6 //完成指定的异步接收操作
7 Message message = myQueue.EndReceive(asyncResult.AsyncResult);
8 signal.Set();
9 Book book = message.Body as Book;
10 Console.WriteLine("图书编号:{0}--图书名称:{1}--图书作者:{2}--图书定价:{3}",
11 book.BookId.ToString(),
12 book.BookName,
13 book.BookAuthor,
14 book.BookPrice.ToString());
15 myQueue.BeginReceive();
16 }
17 catch (MessageQueueException me)
18 {
19 Console.WriteLine("异步接收出错,原因:" + me.Message);
20
21 }
22}
四、MSMQ在邮件发送程序中的应用
在邮件程序的应用中,实现发送邮件的方法有很多种,很多朋友都比较喜欢邮件发送组件(如:JMail),在.NET Framework里也给我们提供了发送邮件的类StmpClient,位于System.Net下。我想很多朋友都曾用到过此类,如果说是在一个小型的邮件应用里,完全没有使用MSMQ的必要,因为数据量不大,直接处理就OK,这里我以邮件程序来说只是出于对MSMQ应用的介绍。说实在的,我也不知道怎么才能把这个东东给介绍清楚,先看看一张图吧!
现在的需求是这样的,有一个邮件发送客户端(SendMail.aspx,界面效果如上图所示)和一个邮件发送管理的服务端(MailServer.aspx),当在SendMail.aspx里发送邮件的时候,我们不直接将其发到目标地址去,而是将其发送到消息队列,然后由MailServer.aspx来负责从消息队列里读取出邮件信息在将其发送到目标地址。其实SendMail.aspx的职责就是完成把邮件信息发送到消息队列,示意性代码如下:
1protected void btnSendMail_Click(object sender, EventArgs e)
2{
3 //取出数据存入MailInfo对象
4 MailInfo info = new MailInfo();
5 info.Title = tbTitle.Text;
6 info.Content = tbContent.Text;
7 info.StmpServer = tbSmtpServer.Text;
8 info.Sender = tbSender.Text;
9 info.SenderPwd = tbSenderPwd.Text;
10 info.ReceiveAddress = tbReceive.Text;
11
12 if (info != null)
13 {
14 CreateQueue();
15 SendMessage(info);
16 }
17}
CreateQueue和SendMessage这两个方法完成消息队列的创建和发送信息到队列,MailInfo类封装的是邮件的详细信息(既邮件的属性),详细定义如下:
封装了邮件的属性
转到邮件管理端(MailServer.aspx),他负责从消息队列里读取出邮件信息并把此邮件发送到目标地址去。其实现很简单,说直接点他也就是完成了两项操作(从队列读消息、将消息发送到目的邮箱),从队列读取消息的代码如下:
1/// <summary>
2/// 连接消息队列并从队列中接收消息
3/// </summary>
4private MailInfo ReceiveMessage()
5{
6 MailInfo info = null;
7 //连接到本地队列
8 MessageQueue myQueue = new MessageQueue(".\\private$\\myMailQueue");
9 myQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(MailInfo) });
10 try
11 {
12 if (myQueue.Transactional)
13 {
14 MessageQueueTransaction myTransaction = new MessageQueueTransaction();
15 //启动事务
16 myTransaction.Begin();
17 //从队列中接收消息
18 Message myMessage = myQueue.Receive(myTransaction);
19 info = myMessage.Body as MailInfo; //获取消息的内容
20 myTransaction.Commit();
21 return info;
22 }
23
24 }
25 catch (MessageQueueException e)
26 {
27 this.tdError.InnerText = e.Message;
28 }
29 return info;
30}
该方法(ReceiveMessage)返回的是从队列里读取到的邮件信息,本示例中只是做了读一条信息的实现,如果要把队列里的全部信息读出并发送到目的邮箱,可以参考我前面所介绍的相关知识点来实现。得到了邮件的详细信息,我们就可以使用相应的技术将这信息发送到目标邮箱去,本示例中我采用的是.NET Framework里提供的SmtpClient类来完成的邮件发送,关于SmtpClient类的使用网上有相当丰富的资料介绍,这里我就不做详细的说明,核心代码如下:
1protected void Button2_Click(object sender, EventArgs e)
2{
3 if (info != null)
4 {
5 SmtpClient client = new SmtpClient();
6 client.Host = info.StmpServer;
7 client.UseDefaultCredentials = false;
8 client.Credentials = new NetworkCredential(info.Sender, info.SenderPwd);
9 client.DeliveryMethod = SmtpDeliveryMethod.Network;
10
11 MailMessage message = new MailMessage(info.Sender, info.ReceiveAddress);
12 message.Subject = info.Title;
13 message.Body = info.Content;
14 message.BodyEncoding = Encoding.UTF8;
15 message.IsBodyHtml = true;
16
17 try
18 {
19 //发送邮件到目标地址
20 client.Send(message);
21 this.tdSucces.InnerText = "邮件已成功发送到目标地址:" + info.ReceiveAddress;
22 }
23 catch (Exception ex)
24 {
25 this.tdError.InnerText = "发送失败,失败原因:" + ex.Message;
26 }
27 }
28}
运行后的效果图如下:
本文中的所有示例程序全部通过调试,能力有限,文中所介绍的不是很清楚,详细可直接下载源代码了解。本代码包里也包含有第一篇文章里的全部示例程序代码。
点击连接下载示例程序代码:示例程序代码下载
分类: MSMQ
本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/4029957.html,如需转载请自行联系原作者