/// <summary> /// 取消异步邮件发送 /// </summary> public void SendAsyncCancel() { // 因为此类为非线程安全类,所以 SendAsyncCancel 和发送邮件方法中操作MessageQueue部分的代码肯定是串行化的。 // 所以不存在一边入队,一边出队导致无法完全取消所有邮件发送 // 1、清空队列。 // 2、取消正在异步发送的mail。 // 3、设置计划数量=完成数量 // 4、执行 AutoDisposeSmtp() if (m_IsAsync) { // 1、清空队列。 MailUserState tempMailUserState = null; while (MessageQueue.TryDequeue(out tempMailUserState)) { Interlocked.Decrement(ref m_messageQueueCount); MailMessage message = tempMailUserState.CurMailMessage; this.InnerDisposeMessage(message); } tempMailUserState = null; // 2、取消正在异步发送的mail。 m_SmtpClient.SendAsyncCancel(); // 3、设置计划数量=完成数量 PrepareSendCount = CompletedSendCount; // 4、执行 AutoDisposeSmtp() this.AutoDisposeSmtp(); } else { throw new Exception(MailValidatorHelper.EMAIL_ASYNC_CALL_ERROR); } }
/// <summary> /// 声明在 SmtpClient.SendAsync() 执行完后释放相关对象的回调方法 最后触发的委托 /// </summary> protected void SendCompleted4Dispose(object sender, AsyncCompletedEventArgs e) { MailUserState state = e.UserState as MailUserState; if (state.CurMailMessage != null) { MailMessage message = state.CurMailMessage; this.InnerDisposeMessage(message); state.CurMailMessage = null; } if (state.IsSmpleMail) { if (state.AutoReleaseSmtp && state.CurSmtpClient != null) { #if DEBUG Debug.WriteLine("释放SmtpClient"); #endif state.CurSmtpClient.Dispose(); state.CurSmtpClient = null; } } else { if (!e.Cancelled) // 取消的就不计数 { CompletedSendCount++; } if (state.AutoReleaseSmtp) { this.AutoDisposeSmtp(); } // 若批量异步发送,需要设置信号 #if DEBUG Debug.WriteLine("Set" + Thread.CurrentThread.ManagedThreadId); #endif AutoResetEvent.Set(); } // 先释放资源,处理错误逻辑 if (e.Error != null && !state.IsErrorHandle) { throw e.Error; } }
/// <summary> /// 发送Email /// </summary> private void InnerSendMessage() { bool hasError = false; MailMessage mMailMessage = null; #region 构建 MailMessage try { mMailMessage = new MailMessage(); mMailMessage.From = new MailAddress(From, FromDisplayName); this.InnerSetAddress(EmailAddrType.To, mMailMessage); this.InnerSetAddress(EmailAddrType.CC, mMailMessage); this.InnerSetAddress(EmailAddrType.Bcc, mMailMessage); mMailMessage.Subject = Subject; mMailMessage.Body = Body; if (m_Attachments != null && m_Attachments.Count > 0) { foreach (Attachment attachment in m_Attachments) { attachment.NameEncoding = Encoding.UTF8; mMailMessage.Attachments.Add(attachment); } } //Encoding chtEnc = Encoding.BigEndianUnicode; mMailMessage.SubjectEncoding = Encoding.UTF8; mMailMessage.BodyEncoding = Encoding.UTF8; // SmtpClient 的 Headers 中会根据 MailMessage 默认设置些值,所以应该为 UTF8 。 mMailMessage.HeadersEncoding = Encoding.UTF8; mMailMessage.IsBodyHtml = IsBodyHtml; if (m_AlternateViews != null && m_AlternateViews.Count > 0) { foreach (AlternateView alternateView in AlternateViews) { mMailMessage.AlternateViews.Add(alternateView); } } mMailMessage.Priority = (MailPriority)Priority; } catch (ArgumentNullException argumentNullEx) { hasError = true; throw argumentNullEx; } catch (ArgumentException argumentEx) { hasError = true; throw argumentEx; } catch (FormatException formatEx) { hasError = true; throw formatEx; } finally { if (hasError) { if (mMailMessage != null) { this.InnerDisposeMessage(mMailMessage); mMailMessage = null; } this.InnerDisposeSmtp(); } } #endregion if (!hasError) { if (m_IsAsync) { #region 异步发送邮件 if (PrepareSendCount == 1) { // 情况一:不重用 SmtpClient 实例会将PrepareSendCount设置为1 // 情况二:计划发送只有一条 // PrepareSendCount 是发送单条邮件。 MailUserState state = new MailUserState() { AutoReleaseSmtp = m_autoDisposeSmtp, CurMailMessage = mMailMessage, CurSmtpClient = m_SmtpClient, IsSmpleMail = true, UserState = AsycUserState, }; if (m_autoDisposeSmtp) { // 由发送完成回调函数根据 IsSmpleMail 字段进行释放 m_SmtpClient = null; } ThreadPool.QueueUserWorkItem((userState) => { // 无需 catch 发送异常,因为是异步,所以这里 catch 不到。 MailUserState curUserState = userState as MailUserState; curUserState.CurSmtpClient.SendAsync(mMailMessage, userState); }, state); } else { // 情况一:重用 SmtpClient 逻辑,即我们可以直接操作全局的 m_SmtpClient // 情况二:批量发送邮件 PrepareSendCount>1 // 情况三:PrepareSendCount 还未设置,为0。比如场景在循环中做些判断,再决定发邮件,循环完才调用 SetBatchMailCount 设置计划邮件数量 MailUserState state = new MailUserState() { AutoReleaseSmtp = m_autoDisposeSmtp, CurMailMessage = mMailMessage, CurSmtpClient = m_SmtpClient, UserState = AsycUserState, }; MessageQueue.Enqueue(state); Interlocked.Increment(ref m_messageQueueCount); if (m_SendMailThread == null) { m_SendMailThread = new Thread(() => { // noItemCount 次获取不到元素,就抛出线程异常 int noItemCount = 0; while (true) { if (PrepareSendCount != 0 && PrepareSendCount == CompletedSendCount) { // 已执行完毕。 this.AutoDisposeSmtp(); break; } else { MailUserState curUserState = null; if (!MessageQueue.IsEmpty) { #if DEBUG Debug.WriteLine("WaitOne" + Thread.CurrentThread.ManagedThreadId); #endif // 当执行异步取消时,会清空MessageQueue,所以 WaitOne 必须在从MessageQueue中取到元素之前 AutoResetEvent.WaitOne(); if (MessageQueue.TryDequeue(out curUserState)) { Interlocked.Decrement(ref m_messageQueueCount); m_SmtpClient.SendAsync(curUserState.CurMailMessage, curUserState); } } else { if (noItemCount >= 10) { // 没有正确设置 PrepareSendCount 值。导致已没有邮件但此线程出现死循环 this.InnerDisposeSmtp(); throw new Exception(MailValidatorHelper.EMAIL_PREPARESENDCOUNT_NOTSET_ERROR); } Thread.Sleep(1000); noItemCount++; } } // SmtpClient 为null表示异步预计发送邮件数已经发送完,在 OnSendCompleted 进行了 m_SmtpClient 释放 if (m_SmtpClient == null) { break; } } m_SendMailThread = null; }); m_SendMailThread.Start(); } } #endregion } else { #region 步发送邮件 try { m_SmtpClient.Send(mMailMessage); m_CompletedSendCount++; } catch (ObjectDisposedException smtpDisposedEx) { throw smtpDisposedEx; } catch (InvalidOperationException smtpOperationEx) { throw smtpOperationEx; } catch (SmtpFailedRecipientsException smtpFailedRecipientsEx) { throw smtpFailedRecipientsEx; } catch (SmtpException smtpEx) { throw smtpEx; } finally { if (mMailMessage != null) { this.InnerDisposeMessage(mMailMessage); mMailMessage = null; } this.AutoDisposeSmtp(); } #endregion } } }