前一阵子做项目的时候遇到一个功能需求:当程序异常或者重大事件时候,发送邮件通知管理员。按理说这是一个很简单的需求,但是在开发当中遇到了一个问题:因为客户那里的网络拓扑是一个需要设置代理才可以访问外网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 发送邮件
在提供的代码中,使用了Interlocked.Increment方法来实现异步编程,该方法可以在多线程环境下安全地递增变量。同时,使用try-catch块来捕获异常,并对异常进行处理。 总结 使用C#的Socket类发送HTTP/HTTPS请求是一...
本文将深入探讨如何在C#中使用Socket类设置代理,以便进行网络通信。首先,我们需要理解Socket类的基本概念,它是网络编程的基础,允许我们通过TCP或UDP协议与远程服务器进行低级别的交互。 ### Socket类简介 ...
标题和描述中的知识点聚焦于如何使用C#的Socket类实现UDP协议通信,这涉及到了UDP协议的基本特性以及在C#中的具体实现方法。以下是对这一主题的深入解析: ### UDP协议简介 用户数据报协议(UDP)是互联网协议族中...
在本示例中,我们关注的是如何利用C#实现一个定时发送邮件的功能。这个功能通常在需要定期向用户发送通知、报告或其他重要信息的场景下非常有用。我们将探讨以下几个关键知识点: 1. **System.Net.Mail**: 这是.NET...
本示例中,我们将探讨如何使用C#的Socket类来实现发送和接收图片的功能。 首先,我们创建一个服务器端,其主要任务是监听客户端的连接请求,并将图片文件发送给客户端。在`Main`方法中,我们执行以下步骤: 1. **...
本项目"**C# socket smtp 邮件发送(支持SSL)**"主要探讨了如何在C#环境中使用Socket通过SMTP协议来发送邮件,并且支持安全的SSL/TLS连接,这在现代网络通信中是必不可少的安全保障。 首先,我们需要理解SMTP的工作...
C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#邮件发送系统C#...
在C#编程中,发送邮件是一项常见的任务,尤其在开发Windows桌面应用(WinForm)时。这个"MailTest"文件可能包含了一个简单的C#程序,用于演示如何利用.NET Framework的System.Net.Mail命名空间来实现邮件发送功能。...
在本文中,我们将深入探讨如何使用C#的Socket编程来实现一个简单的聊天应用程序,其中包含服务器和客户端的交互。Socket编程是网络通信的基础,它允许应用程序通过网络进行数据传输。在C#中,System.Net命名空间下的...
在C#编程环境中,通过TLS(Transport Layer Security)发送邮件是一项常见的任务,特别是在现代网络安全标准日益严格的情况下。TLS协议提供了一种加密通信的方式,确保邮件在传输过程中不被窃取或篡改。以下是对这个...
本文将深入探讨如何使用C#语言通过Socket实现大文件的分包传输,以解决在数据传输过程中可能遇到的网络拥堵、内存限制等问题。 首先,理解Socket的基本概念。Socket是网络通信中的一个端点,它提供了一种在不同...
本文将详细介绍如何在 C# 中使用 Socket 来实现数据的发送与接收。 #### 代码解析及知识点详解 ##### 一、创建 Socket 对象 ```csharp Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ...
在本文中,我们将介绍如何使用 C# 语言通过 163 的 SMTP 服务器发送邮件。首先,我们需要了解 System.Net.Mail.SmtpClient 类,它是 C# 中专门用来请求 SMTP 服务器发送邮件的类。但是,如果我们使用本地的 SMTP ...
首先,Socket在C#中是System.Net.Sockets命名空间下的类,它提供了在网络中进行数据传输的基本功能。在文件传输中,我们通常使用TCP协议,因为其具有可靠性,保证了数据的顺序和完整性。 1. **TCP连接的建立** 在...
提供的C#示例代码展示了如何在.NET环境中使用HPSocket库进行Socket编程。这包括如何创建Socket实例,设置连接参数,发送和接收数据,以及处理各种事件。 7. **C++ DLL**: C++动态链接库(DLL)提供了另一种语言...
在VS2010环境下,开发者可以通过创建控制台应用程序项目来实现这些功能,使用C#的语法编写Socket相关的代码。同时,VS2010还提供了一个强大的调试环境,有助于查找和修复代码中的错误。 为了确保良好的网络通信,...
C# Socket客户端与服务器通讯指南:断线重连与远程文件发送实战教程,C# Socket通信:实现客户端与服务器断线重连及远程文件发送功能简明教程,C# Socket客户端和服务器通讯,具有断线重连功能,还可 以远程发送文件。...
C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件C#发送邮件...
在C#中,Socket的`BeginAcceptSocket`和`EndAcceptSocket`方法可以实现异步接受客户端连接。封装库可能会创建一个线程池,当有新的连接请求时,将该请求放入队列,由线程池中的工作线程处理,避免阻塞主线程,提高...