前一阵子做项目的时候遇到一个功能需求:当程序异常或者重大事件时候,发送邮件通知管理员。按理说这是一个很简单的需求,但是在开发当中遇到了一个问题:因为客户那里的网络拓扑是一个需要设置代理才可以访问外网smtp服务器的网络环境,所以程序在直连外网时候好使,拿到内网就不能用了。于是我在网上找了很多关于使用C#程序发邮件的例子,但很少有关于使用代理方式,特别是支持审核代理方式发送的案例,我分别使用了SMTPClient对象,CDO对象来进行开发,发现.net framework提供的smtpclient对象不支持代理方式发送,cdo里面有些关于代理的设置,但是没有关于访问代理时候的用户名、密码、端口设置的地方,于是问题被搁置下来。后来也请求过微软方面的支持,也没有给出什么好的方案【想让我用webservice,因为那个支持代理的审核,但是既然我能在内网使用foxmail,outlook通过设置代理属性后正常的收发邮件,那为什么还要使用什么webservice呢?看来微软的这些所谓的专家也有很菜的方面】。既然高级的方式使用不了,于是考虑回到原点——使用socket方式发送邮件。废话少说吧,先把代码贴出来大家分享:
1.先声明一个TCPClient对象,用于Socket发送
-
privateTcpClientsendTcp=null;
2.写几个方法用于与SMTP服务器的交互
-
privatevoidMailSocketAlternation(string[]mailto,stringsubject,stringmsg,stringattachpath)
- {
-
boolcheck=false;
-
NetworkStreamstream=sendTcp.GetStream();
- #region发送Hello握手
-
stringhostName=Dns.GetHostName();
-
check=SendCommand(refstream,"EHLO"+hostName,"EHLO","250");
-
intround=0;
-
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"EHLO"+hostName,"EHLO","250");
- }
- #endregion
- #region请求审核登录
-
check=SendCommand(refstream,"AUTHLOGIN","AUTHLOGIN","334");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"AUTHLOGIN","AUTHLOGIN","334");
- }
- #endregion
- #region身份验证
-
check=SendCommand(refstream,Convert.ToBase64String(Encoding.Default.GetBytes(clsParam.Param.SendMailAccount)),"用户名","334");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,Convert.ToBase64String(Encoding.Default.GetBytes(clsParam.Param.SendMailAccount)),"用户名","334");
- }
-
if(!check)
- {
-
thrownewException("邮件帐户身份验证失败!");
- }
-
check=SendCommand(refstream,Convert.ToBase64String(Encoding.Default.GetBytes(clsParam.Param.SendMailPWD)),"密码","235");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"EHLO"+hostName,"EHLO","250");
-
intround0=0;
-
while(!check&&round0<5)
- {
- round0++;
-
check=SendCommand(refstream,"EHLO"+hostName,"EHLO","250");
- }
-
check=SendCommand(refstream,"AUTHLOGIN","AUTHLOGIN","334");
-
check=SendCommand(refstream,Convert.ToBase64String(Encoding.Default.GetBytes(clsParam.Param.SendMailAccount)),"用户名","334");
-
check=SendCommand(refstream,Convert.ToBase64String(Encoding.Default.GetBytes(clsParam.Param.SendMailPWD)),"密码","235");
- }
-
if(!check)
- {
-
thrownewException("邮件帐户身份验证失败!");
- }
- #endregion
- #region发件人
-
check=SendCommand(refstream,"MAILFROM:<"+clsParam.Param.SendMailAccount+">","MAILFROM","250");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"MAILFROM:<"+clsParam.Param.SendMailAccount+">","MAILFROM","250");
- }
-
#endregion
- #region收件人
-
check=SendCommand(refstream,"RCPTTO:<"+mailto[0]+">","RCPTTO","250");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"RCPTTO:<"+mailto[0]+">","RCPTTO","250");
- }
- #endregion
- #region抄送人
-
if(mailto.Length>1)
- {
-
for(inti=1;i<mailto.Length;i++)
- {
-
check=SendCommand(refstream,"RCPTTO:<"+mailto[i]+">","RCPTTO","250");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"RCPTTO:<"+mailto[i]+">","RCPTTO","250");
- }
- }
- }
- #endregion
- #region密送人
- //这里看大家是否需要了,可以偷偷给自己发一份,呵呵
- #endregion
- #region请求发送邮件体
-
check=SendCommand(refstream,"DATA","DATA","354");
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,"DATA","DATA","354");
- }
- #endregion
- #region发送邮件头
-
StringBuildermailhead=newStringBuilder();
-
mailhead.Append("Subject:"+subject)
-
.Append("/nDate:"+DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss.fff"))
-
.Append("/nFrom:"+"上报软件<"+clsParam.Param.SendMailAccount+">")
-
.Append("/nTo:"+mailto[0]);
-
if(mailto.Length>1)
- {
-
mailhead.Append("/nCc:");
-
for(inti=1;i<mailto.Length;i++)
- {
-
mailhead.Append(mailto[i]+";");
- }
- }
-
mailhead.Append("/nBcc:a@b.com/n/n"); //这个地方大家随便改,注意这只是头,而不是真正的接收者,客户端只会解释,而不会发送,发送的是完全由RCPT TO控制的
- #endregion
- #region发送邮件内容
-
check=SendCommand(refstream,mailhead.ToString()
- +msg
-
+"/n/r/n./r/n","信已发出,服务器","250",false);
- round=0;
-
while(!check&&round<5)
- {
- round++;
-
check=SendCommand(refstream,mailhead.ToString()
- +msg
-
+"/n/r/n./r/n","信已发出,服务器","250",false);
- }
- #endregion
- }
-
privateboolSendCommand(refNetworkStreamnetstream,stringcontent,stringrehead,stringreflag)
- {
-
returnSendCommand(refnetstream,content,rehead,reflag,true);
- }
-
privateboolSendCommand(refNetworkStreamnetstream,stringcontent,stringrehead,stringreflag,boolisNewLine)
- {
-
boolretBool=false;
-
try
- {
-
WriteToNetStream(refnetstream,content,isNewLine);
-
stringcome=ReadFromNetStream(refnetstream);
-
AddLog(rehead+"应答:"+come+"/r/n");
- retBool=CheckForError(come,reflag);
- }
-
catch
- {
-
retBool=false;
- }
-
returnretBool;
- }
-
privatevoidWriteToNetStream(refNetworkStreamNetStream,stringCommand)
- {
-
WriteToNetStream(refNetStream,Command,true);
- }
-
privatevoidWriteToNetStream(refNetworkStreamNetStream,stringmessage,boolisNewLine)
- {
-
stringstringToSend=isNewLine?message+"/r/n":message;
-
byte[]arrayToSend=Encoding.Default.GetBytes(stringToSend.ToCharArray());
- NetStream.Write(arrayToSend,0,arrayToSend.Length);
- }
-
privatestringReadFromNetStream(refNetworkStreamNetStream)
- {
-
byte[]temp=newbyte[512];
- NetStream.Read(temp,0,temp.Length);
-
returnEncoding.Default.GetString(temp);;
- }
-
privateboolCheckForError(stringstrMessage,stringcheck)
- {
-
if(strMessage.IndexOf(check)==-1)
- {
-
returnfalse;
- }
-
else
- {
-
returntrue;
- }
- }
3.再包一层方法
-
privateboolSendMail(stringsubject,stringmsg,stringattachpath)
- {
-
boolretbool=false;
-
try
- {
-
this.btnSendTestMail.Enabled=false;
-
string[]mails=this.txtNoticeMailAddress.Text.Split(";".ToCharArray(),StringSplitOptions.RemoveEmptyEntries);//收件人用;隔开
-
if(clsParam.Param.IsProxy)
- {
-
switch(clsParam.Param.ProxyType)
- {
-
caseMailProxyType.HTTP:
-
#regionHTTP方式处理
-
if(sendTcp==null)
- {
-
stringauthstr=clsParam.Param.ProxyUID+":"+clsParam.Param.ProxyPWD;
-
stringauthproxy="CONNECT"+clsParam.Param.SendMailSmtpAdd+":"+clsParam.Param.SendMailSmtpPort
-
+"HTTP/1.0/r/nProxy-Authorization:Basic"
-
+Convert.ToBase64String(Encoding.Default.GetBytes(authstr))+"/r/n/r/n";//代理服务器审核
-
booltestproxyflag=true;//测试时候使用,设置成false可以不使用代理的Socket方式发邮件
-
if(testproxyflag)
- {
-
sendTcp=newTcpClient(clsParam.Param.ProxyIP,clsParam.Param.ProxyPort);
- }
-
else
- {
-
sendTcp=newTcpClient(clsParam.Param.SendMailSmtpAdd,clsParam.Param.SendMailSmtpPort);
- }
- NetworkStreamstream=sendTcp.GetStream();
-
-
if(testproxyflag)
- {
-
WriteToNetStream(refstream,authproxy,false);
- }
-
-
stringresponse=ReadFromNetStream(refstream);
-
AddLog("连接应答:"+response+"/r/n");
-
boolcheck=false;
-
if(testproxyflag)
- {
-
check=CheckForError(response,"HTTP/1.0200")||
-
CheckForError(response,"HTTP/1.1200");
-
if(check)
- {
-
AddLog("邮件代理连接成功/r/n");
- }
-
else
- {
-
thrownewException("邮件代理连接失败/r/n");
- }
-
stringreceive=ReadFromNetStream(refstream);//这个一定要有,自己在这里走了点弯路
-
AddLog("远端服务器连接应答:"+receive+"/r/n");
-
check=CheckForError(receive,"220");
-
if(check)
- {
-
AddLog("远端服务器连接成功/r/n");
- }
-
else
- {
-
thrownewException("远端服务器连接失败/r/n");
- }
- }
-
else
- {
-
check=CheckForError(response,"220");
-
if(check)
- {
-
AddLog("邮件服务器连接成功/r/n");
- }
-
else
- {
-
thrownewException("邮件服务器连接失败/r/n");
- }
- }
- }
-
try
- {
-
- MailSocketAlternation(mails,subject,msg,attachpath);
- }
-
catch(Exceptionex)
- {
- sendTcp.Close();
-
sendTcp=null;
-
throwex;
- }
-
finally
- {
-
if(sendTcp!=null)
- {
-
try
- {
- NetworkStreamstream=sendTcp.GetStream();
-
WriteToNetStream(refstream,"QUIT");
-
AddLog("QUIT应答:"+ReadFromNetStream(refstream)+"/r/n");
- }
-
catch(Exceptionex)
- {
-
throwex;
- }
-
finally
- {
- sendTcp.Close();
-
sendTcp=null;
- }
- }
- }
- #endregion
-
break;
-
caseMailProxyType.SOCKS4: //此种方式我软件没让他支持,如果大家想用可以使用CDO的方法试一下,我也没有试是否可行,但是Socket的方式肯定是可以的,只是要有一些变化,如果有人感兴趣可以给我留言。使用CDO需要在项目里引用Microsoft CDO for Windows 2000 Library和Microsoft
ActiveX Data Objects 2.8 Library两个COM
- #regionSOCKS4方式处理--暂时不予支持
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #endregion
-
break;
-
caseMailProxyType.SOCKS5: //同Socket4的注释
- #regionSOCKS5方式处理--暂时不予支持
- #endregion
-
break;
- }
- }
-
else
- { //这种方式我也列出来,方便大家参考,如果能直连外网,这个方式最简单
- #region标准方式发送邮件
-
System.Net.Mail.MailMessagemail=newSystem.Net.Mail.MailMessage();
-
SmtpClientSmtpServer=newSmtpClient();
-
SmtpServer.Credentials=newSystem.Net.NetworkCredential(clsParam.Param.SendMailAccount,clsParam.Param.SendMailPWD);
- SmtpServer.Port=25;
- SmtpServer.Host=clsParam.Param.SendMailSmtpAdd;
-
SmtpServer.EnableSsl=false;
-
mail.From=newMailAddress(clsParam.Param.SendMailAccount,"上报软件",System.Text.Encoding.Default);
- mail.To.Add(mails[0]);
-
if(mails.Length>1)
- {
-
for(inti=1;i<mails.Length;i++)
- {
- mail.CC.Add(mails[i]);
- }
- }
-
mail.Bcc.Add("a@b.com");
-
if(File.Exists(attachpath))
- {
-
mail.Attachments.Add(newAttachment(attachpath));
- }
-
mail.IsBodyHtml=false;
- mail.BodyEncoding=Encoding.Default;
- mail.SubjectEncoding=Encoding.Default;
- mail.DeliveryNotificationOptions=DeliveryNotificationOptions.OnFailure;
- mail.Subject=subject;
- mail.Body=msg;
- SmtpServer.Send(mail);
- #endregion
- }
-
AddLog("邮件发送成功");
-
retbool=true;
- }
-
catch(Exceptionex)
- {
-
AddLog("发送邮件异常:"+ex.Message);
-
retbool=false;
- }
-
finally
- {
-
this.btnSendTestMail.Enabled=true;
- }
-
returnretbool;
- }
4.下面我们测试一下
-
privatevoidbtnSendTestMail_Click(objectsender,EventArgse)
- {
-
if(SendMail("【系统自动邮件】系统运行情况通知","这是一封测试邮件",null))
- {
-
MessageBox.Show("测试邮件发送成功!","",MessageBoxButtons.OK,MessageBoxIcon.Information);
- }
-
else
- {
-
MessageBox.Show("测试邮件发送失败!","",MessageBoxButtons.OK,MessageBoxIcon.Error);
- }
- }
程序写的匆忙,注释少了一些,不过应该还方便看出思路,上面我分别列出了三种发送邮件的方式,其中Socket的方式又可以采用三种代理方式来搞定(我只列出了一种HTTP方式的,另两种可以和我联系)。关于SMTP交互的方式,我参考了RFC821的文档,如果E文不好,可以参考中文的,对于发送方法还借鉴了微软社区的这篇文章。另外,对于通过审核代理方式可以参考这篇网文,关于CdoConfiguration
Module可以参考这篇资料。在Socket方法里我没有实现附件的功能,因为我那里不需要,如果大家想用,可以留言联系。
希望这些代码可以对一些有相同问题困扰的Coder们有所帮助,有问题大家留言探讨。
原文链接:http://blog.csdn.net/luyifeiniu/article/details/3320651
分享到:
相关推荐
C# WinForm 代理上网状态下发送邮件 Socket 发送邮件
C# 基于SMTP协议和SOCKET发送邮件及附件。 在WIN7的VS2010环境下编译测试OK。
主要介绍了c#使用Socket发送HTTP/HTTPS请求的实现代码,需要的朋友可以参考下
C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#...
C#的Socket实现UDP协议通信 CSharp
c# socket设置代理,经测试没有问题,才敢发帖.
C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送邮件代码 C# 发送...
WPF实现CS结构,基于socket连接,实现Server<->Client发送消息
C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件...
一个完整的C#发送邮件的实例,有完整代码,供新手参考。
C# socket smtp 邮件发送(支持SSL)源码
C#使用socket发送(接收)文件的程序,任何文件均可。 其中SingleSendForm是一次发送一个文件,MultiSendForm可以一次选择多个文件,然后依次发送。
C#使用socket发送和接受数据,一看就懂~~~非常适合新手!
本邮件发送器实现Html格式发送(即跟网页登录邮件发送一样),跟很多高级邮件软件和网页邮件登录在线编编辑一样,完全实现“所见即所得”的效果,本软件虽小,但功能包括了绝大部分的一般正常电子邮件所用到的功能。...
主要介绍了C#使用Socket实现发送和接收图片的方法,涉及C#操作socket发送与接收文件的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
发送电子邮件(c#),c#开发发送电子邮件
C# Socket服务端向指定的客户端发送消息(包含服务器)
前段时间公司有项目需要使用C#的SMTP 发邮件功能,在网上搜索了下,网上没有一个完整的解决方案,都是东说语句西说一句,几乎扯淡,对于我们这些伟大的程序员来说,就是完整的解决版本,所以直接上代码是最现实。...
c#实现SMTP发送邮件功能,可以发送,抄送给多人,并可以发送附件。
net游戏编程源入门经典——C#篇 文档和代码