紧接着上一篇文章“WPF邮件群发工具开发 之 进度条(属性改变通知机制)的实现”,这篇博文想把在工具开发中的实现要点跟大家交流、分享下,首先要说的是这个工具算是个Demo,实现的方法和功能都比较简单,还有不少细节需要完善...
本文目录:
如何随机生成真实存在的QQ邮箱帐号?
经过测试,通过C#代码发送邮件(调用SmtpClient类的Send方法,此方法无返回值,只会在发送邮箱账户错误或邮箱账户设置有问题,更多的是你用一个发送邮箱账户做群发,因为每种邮箱都有其这方面的限制,在连续很短时间内,只允许接收同一个邮箱发送的限量的邮件,如:QQ的限制大概50个左右),即使给一个根本不存在的邮箱里发送邮件,不会抛出异常,也更无法知道是否已发送成功,只能默认为发送成功,这样一说,似乎随机生成真实存在的QQ邮箱帐号是无法实现的事。——其实不然,可以用一个或几个qq邮箱账户检测生成的QQ邮箱是否真实存在! 代码实现如下:
1 /// <summary> 2 /// 生成邮箱的事件处理 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 void btnGenerate_Email_Click(object sender, RoutedEventArgs e) 7 { 8 #region 判断 9 string str = this.txtGenerateEmailCount.Text.Trim(); 10 if (string.IsNullOrEmpty(str)) 11 { 12 MessageBox.Show("请输入要生成的邮箱个数!"); 13 return; 14 } 15 int generate_Email_Count = 0; 16 int.TryParse(str, out generate_Email_Count); 17 if (generate_Email_Count <= 0) 18 { 19 MessageBox.Show("邮箱个数必须为大于0!"); 20 return; 21 } 22 EmailAccount emailAccount = this._emailAccountList.Find(E => E.Smtp == "smtp.qq.com"); 23 if (emailAccount == null) 24 { 25 MessageBox.Show("qq邮箱账户不存在!"); 26 return; 27 } 28 #endregion 29 this._sendEmailsPropertyChanged.PropertyData += this.GetInfoTip("生成QQ邮箱"); 30 EmailInfoEntity emailInfoEntity = new EmailInfoEntity() 31 { 32 Title = this.txtEmailTitle.Text, 33 Body = this.txtEmailContent.Text, 34 DisplayName = this.txtDisplayName.Text.Trim(), 35 Attachment = _accessoriesFilePath, 36 Host = emailAccount.Smtp, 37 From = emailAccount.Email, 38 Pwd = emailAccount.Pwd 39 }; 40 #region 线程处理 41 this.StartSingleThreadWork(() => 42 { 43 int i = 0, num; 44 string email = string.Empty; 45 System.Random random = new Random(); 46 bool isSuc; 47 while (i < generate_Email_Count) 48 { 49 num = random.Next(123456, 987654321); 50 email = string.Format("{0}@qq.com", num); 51 if (this._emailList.Contains(email)) 52 continue; 53 emailInfoEntity.To = email; 54 isSuc = EmailHelper.SendEmail(emailInfoEntity); 55 Thread.Sleep(1000); 56 if (!isSuc) 57 { 58 _sendResult.SendFailEmails += string.Format("【{0}】:发送失败!{1}", email, Environment.NewLine); 59 continue; 60 } 61 this._emailList.Add(email); 62 this._sendEmailsPropertyChanged.PropertyData += email + Environment.NewLine; 63 i++; 64 Console.WriteLine(email); 65 } 66 MessageBox.Show("Generate Over"); 67 }); 68 #endregion 69 }
单线程的使用及注意
1 /// <summary> 2 /// 开始一个单线程工作 3 /// </summary> 4 /// <param name="threadStartHandle"></param> 5 private void StartSingleThreadWork(ThreadStart threadStartHandle) 6 { 7 _thread = new Thread(() => 8 { 9 //禁用发送按钮 10 _sendResult.SendControlIsEnabled = false; 11 threadStartHandle(); 12 //启用发送按钮 13 _sendResult.SendControlIsEnabled = true; 14 _thread.Abort(); 15 _thread = null; 16 Console.WriteLine(threadStartHandle.Method.Name);//---此行代码不会执行,因为它位于 线程终止Abort方法之后,即Abort方法通知当前线程的使命已完成,其它的后续工作它将不会再执行! 17 }); 18 _thread.Start(); 19 }
除了上面代码中的注释说明,还有一点需要重点指出的是:.Net的公用语言运行时(CLR)能区分两种不同类型的线程:前台线程和后台线程。这两者的区别就是:应用程序必须运行完所有的前台线程才可以退出;而对于后台线程,应用程序则可以不考虑其是否已经运行完毕而直接退出,所有的后台线程在应用程序退出时都会自动结束。所以,当应用程序的窗体关闭时,你希望结束所有的线程,你可以把线程设置为后台线程,或在窗体关闭事件中检查是否存在的执行中的线程并对其销毁!
群发工具开发综述
为了提高邮件发送的命中率,基于上面所提到的问题,应该采用多个邮箱账户轮发。
核心代码如下:
1 void btnSend_Click(object sender, RoutedEventArgs e) 2 { 3 #region 判断 4 if (this._emailList == null || this._emailList.Count == 0) 5 { 6 MessageBox.Show("当前要发送的邮箱为空!"); 7 return; 8 } 9 string eamilTitle = this.txtEmailTitle.Text.Trim(); 10 if (string.IsNullOrEmpty(eamilTitle)) 11 { 12 MessageBox.Show("请输入email标题!"); 13 return; 14 } 15 string emailContent = this.txtEmailContent.Text.Trim(); 16 if (string.IsNullOrEmpty(emailContent)) 17 { 18 MessageBox.Show("请输入email内容!"); 19 return; 20 } 21 if (!string.IsNullOrEmpty(_accessoriesFilePath)) 22 { 23 if (!System.IO.File.Exists(_accessoriesFilePath)) 24 { 25 MessageBox.Show("请确认所选附件是否存在!"); 26 return; 27 } 28 } 29 #endregion 30 string displayName = this.txtDisplayName.Text.Trim(); 31 int count = this._emailList.Count; 32 this.progressBar.Maximum = count; 33 #region 发送处理 34 this.StartSingleThreadWork(() => 35 { 36 Console.WriteLine("The send count is :" + count); 37 bool isSuc; 38 int failCount = 0, i = 1; 39 string failTip = string.Empty; 40 foreach (string email in this._emailList) 41 { 42 _sendResult.CurrentSendNum = i; 43 _sendResult.ProgressBarNumShow = i + "/" + count; 44 _sendCount = 0; 45 _sendAccountLastIndex = -1; 46 isSuc = SendEmail(new EmailInfoEntity() 47 { 48 DisplayName = displayName, 49 Body = emailContent, 50 Title = eamilTitle, 51 Attachment = _accessoriesFilePath, 52 To = email 53 }); 54 i++; 55 if (!isSuc) 56 { 57 failTip = string.Format("【{0}】:发送失败!{1}", email, Environment.NewLine); 58 _sendResult.SendResultMes += failTip; 59 _sendResult.SendFailEmails += failTip; 60 failCount++; 61 } 62 System.Threading.Thread.Sleep(1000); 63 } 64 string tipStr = ("failCount is :" + failCount); 65 _sendResult.SendResultMes += tipStr; 66 MessageBox.Show(tipStr); 67 }); 68 #endregion 69 } 70 71 private bool SendEmail(EmailInfoEntity emailInfoEntity) 72 { 73 if (emailInfoEntity == null) 74 throw new ArgumentNullException("emailInfoEntity is Null"); 75 int sendAccountIndex = GetSendAccountIndex(); 76 EmailAccount emailAccount = _emailAccountList[sendAccountIndex]; 77 emailInfoEntity.Host = emailAccount.Smtp; 78 emailInfoEntity.From = emailAccount.Email; 79 emailInfoEntity.Pwd = emailAccount.Pwd; 80 bool isSuc = EmailHelper.SendEmail(emailInfoEntity); 81 _sendAccountLastIndex = sendAccountIndex; 82 _sendResult.SendResultMes += string.Format("★From:{0}★To:{1}★---isSuc:{2}!{3}", emailInfoEntity.From, emailInfoEntity.To, isSuc, Environment.NewLine); 83 _sendCount++; 84 if (!isSuc) 85 { 86 //如果发送失败且已发送次数_sendCount小于_emailAccountListCount,则再次发送 87 if (_sendCount < _emailAccountListCount) 88 { 89 //当前线程挂起1s,再次发送 90 System.Threading.Thread.Sleep(1000); 91 return SendEmail(emailInfoEntity); 92 } 93 } 94 return isSuc; 95 } 96 97 /// <summary> 98 /// 获得当前发送邮件的邮箱账户的index 99 /// </summary> 100 /// <returns></returns> 101 private int GetSendAccountIndex() 102 { 103 if (_emailAccountListCount > 1) 104 { 105 int sendAccountIndex = new Random().Next(_emailAccountListCount); 106 if (_sendAccountLastIndex >= 0 && sendAccountIndex == _sendAccountLastIndex) 107 return GetSendAccountIndex(); 108 return sendAccountIndex; 109 } 110 return 0; 111 }
工具运行效果图如下:
好了,本文到此结束,希望有这么经验或更好的建议的朋友,能够多多交流!