Why does SmtpClient.SendAsync hang? - c#

I am using SmtpClient.SendAsync (C#) to send emails in ASP.NET web applications and services. The web application/service runs on Windows Server 2012 R2 using IIS 8.
There are times where the call to SendAsync hangs up and does not appear to send the mail message asynchronously, so the calling thread blocks. This behavior seems to be sporadic. I cannot replicate the problem in a testing environment. This is especially problematic when sending the email as a result of a call to a web method because the timeout is 60 seconds (I'm using SendAsync for this very reason so the client doesn't experience any time delay).
Here is my code snippet.
SmtpClient client;
MailMessage msg;
public void SendMail()
{
try
{
client = new SmtpClient("smtpAddress#mydomain.com");
msg = new MailMessage();
msg.Subject = "Test";
msg.Body = "This is the body";
msg.From = "noreply#example.com";
msg.To.Add("me#example.com");
client.SendCompleted += new SendCompletedEventHandler(sendCompletedCallback);
client.SendAsync(msg, "Test");
}
catch (Exception ex)
{
// Log error
}
}
private void sendCompletedCallback(object send, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// Log error
}
else if (e.Cancelled)
{
// Log cancellation
}
else
{
// Log success
}
client.Dispose();
msg.Dispose();
}
Why does the call to SendAsync hang and block the calling thread at times?

Please check below link,
https://msdn.microsoft.com/en-us/library/x5x13z6h(v=vs.110).aspx
There might be two case, either it is not waiting for e-mail transmission to complete before attempting to send another e-mail message
or
recipients is invalid

Related

Sending mails from WCF application via smtp-relay

I'm not sure if this is the correct place to ask this question. But in my solution we have encountered a problem with sending mails.
Tech details:
I telnet the smtp-relay and get a response.
We send some emails from the application and it works.
And after a while the messages does not get sent and I can't telnet the smtp-server.
Then we need to restart the server and it works for a while.
The code that sends the message is:
protected virtual void Send(Email mailMessage)
{
using (SmtpClient client = new SmtpClient(this._mailService.SmtpContext.SmtpServer, this._mailService.SmtpContext.SmtpPort))
{
client.EnableSsl = _mailService.SmtpContext.EnableSsl;
if (_mailService.SmtpContext.UseDefaultCredentials)
{
client.UseDefaultCredentials = true;
}
else
{
NetworkCredential credentials = new NetworkCredential(this._mailService.SmtpContext.Username, this._mailService.SmtpContext.Password);
client.Credentials = credentials;
}
client.DeliveryMethod = SmtpDeliveryMethod.Network;
try
{
client.Send(mailMessage);
}
catch (SmtpException e)
{
/// LOG
}
/// Trigger mail sent event
onMailSent(this, mailMessage);
}
}
I would like to know if anyone has faced an issue like this, and if there exist a solution.
We can see a connection being created in netstat, but it doesn't get removed. Is it some kind of error in the smptclient disposal?

How to send Newsletters (bulk emails) asynchronously in ASP.NET

I need to send about 5000 emails as newsletter to my website users asynchronously.. The problem is i don't know the best way to send them asynchronously ..Please help me updating my code to make it asynchronously
My Code:
public string SendEmail()
{
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
client.Send(message);
}
return "done";
}
Thank you , Lucy
Take a look at the async and await keywords.
https://msdn.microsoft.com/en-us/library/mt674882.aspx
The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in the .NET Framework or the Windows Runtime to create an asynchronous method almost as easily as you create a synchronous method. Asynchronous methods that you define by using async and await are referred to as async methods.
MSDN explains the syntax side of things. The bigger concern is error handling and reliably. Dumping 5,000 emails into a list and hitting the "send" button on them is a little optimistic. Do these emails need to be reliably delivered? What happens if 3,000 of them send, and a network error suddenly causes temporary connectivity loss to the outgoing mail server? Are you going to resend all 5,000 when it starts working again? Just forget about the last 2,000? Are the recipients going to be mad because they got duplicates, or didn't get the message at all? How are you going to troubleshoot errors?
A pattern that I've found that has worked really well (whether you are sending synchronously or asynchronously), is to generate the messages and store each in a database table, and then use something like the following:
public void SendAllEmails()
{
var emails = SomeClass.GetAllUnsentEmails();
foreach(Email message in Emails)
{
var success = SendEmail(message);
if (!success)
{
// Do you want to do something if it fails?
}
}
}
public bool SendEmail(Email message)
{
try
{
// 1. Send the email message
// 2. Update the "SentOn" date in the database
// 3. return true
}
catch(Exception ex)
{
SomeClass.CreateEmailErrorEntry(message, ex); // store error in a table or log
return false;
}
}
edit: if it's being used inside of action you should mark your action async
public async Task<ActionResult> MyAction()
for a start, don't create SmtpClient for every message so as follows
and also this, should send the mails asynchronously but wait for all of them to be sent
public string SendEmail()
{
var tasks = new List<Task>();
var client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
tasks.Add(client.SendMailAsync(message));
}
while(tasks.Count > 0)
{
var idx = Task.WaitAny(tasks.ToArray());
tasks.RemoveAt(idx);
}
return "done";
}

How to complete request before redirecting

Using C#/Asp.Net
I have an application that goes out to a web service. On return there's a couple of things that happen:
void Cleanup(Response response)
{
// My web service takes up to 30 seconds
// then this method is called
// I send this email
var email = SaleEmail.Create(
response.ID
DateTime.Now,
"A sale was made!");
email.Send();
// Then redirect
Response.Redirect(response.RedirectUrl, false);
Context.ApplicationInstance.CompleteRequest();
}
The idea is, on completion of the web service an email is sent, then the page is redirected.
Previously, I used a normal redirect - the result was that 90% of the emails were never sent.
I've changed the redirect pattern, however it's still not perfect - I'm guessing 25% of emails are still not coming through.
Anyone advise any improvements to the pattern I have?
Email code:
public static void Send(MailMessage message)
{
Guard.Argument.NotNull(() => message);
var c = new SmtpClient();
try
{
c.Send(message);
}
catch (Exception ex)
{
}
finally
{
c.Dispose();
message.Dispose();
}
}
Maybe
Try to implement async task method with sendAsync and await
this await will help you to wait how much needed to send email before jump to redirect
//async Task
public async Task Cleanup(Response response)
{
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendAsync();...//await
}
}
you should rewrite your initialization somehow, make it look like this:
smtpClient.SendAsync();
smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
on smtpClient_SendCompleted function write your redirection code

smtpclient is not initiating smtp traffic

We have two versions of a web application running in IIS 7.5 on Windows Server 2008R2, with almost identical running conditions. Both versions are using the same set of email code to relay smtp messages to an internal Exchange2010 server. The first application completes the request, and sends the email without a hitch. However, the second, which uses a custom security model (being the only major difference between the apps), will not even initiate an smtp request at all. There is code to handle exceptions from SMTPClient.send, but none are occurring. Both versions send mail from development machine running with visual studio application server.
Wireshark shows complete request and verification of credentials for application one, but no traffic is detected at all from application two. The virtual running the IIS instance has it's IP address accepted by the Exchange server, and is on the same domain etc. The firewall is turned off for the domain.
UseDefaultCredentials is being implemented for authentication of smtp messages. Each application is running inside a separate app pool, but are using the same user (NetworkService) and have identical security permissions on the wwwroot etc.
Any insight is appreciated as to why the one application is seemingly doing nothing when using .Net Mail.SMTPClient on the IIS 7.5 server.
Code in a nutshell is as follows (ignore possible method signature mismatches, code is in different classes, default SMTP host is defined by Email.Host)
protected void taskHeaderGrid_OnItemCommand(object sender, GridCommandEventArgs e)
{
try {
Int32 recordsAcivated = workFlowController.activateWorkFlow((IDbConnection)myConn, null, System.Convert.ToInt32(taskHdrIdTxt));
}
catch (Exception) {
AddMessage(Message.eMessageType.Error, "Warning: email message to alert users that next task has been activated was not sent.");
}
}
public Int32 activateWorkFlow(IDbConnection currConnection, IDbTransaction currTran, Int32 workFlowId)
{
//Send out the mails......
if (!string.IsNullOrWhiteSpace(primaryPersonEmail))
{
emailController.sendMessage(primaryPersonEmail, "Task has been activated.", string.Format("Hdr:({0}) Detail:({1})", taskHeaderDTO.Description, jobFunctionDTO.JobDescription));
}
if (!string.IsNullOrWhiteSpace(secondaryPersonEmail))
{
emailController.sendMessage(secondaryPersonEmail, "Task has been activated.", string.Format("Hdr:({0}) Detail:({1})", taskHeaderDTO.Description, jobFunctionDTO.JobDescription));
}
}
public void sendMessage(string toEmailAddr, string txtmessage, string subject)
{
Email.Host = hostName;
Email.Send(subject, txtmessage, senderEmailAddr, toEmailAddr);
}
public static void Send(string subject, string body, string from, IEnumerable<string> to, string smtphost)
{
Send(CreateMessage(subject, body, from, to), smtphost);
}
public static void Send(System.Net.Mail.MailMessage msg, string smtphost)
{
using (System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient(smtphost))
{
if (InterceptEmails == true)
{
ApplyFailSafe(msg);
}
client.UseDefaultCredentials = true;
client.Send(msg);
}
}
public static System.Net.Mail.MailMessage CreateMessage(string subject, string body, string from, IEnumerable<string> to, IEnumerable<string> cc, IEnumerable<string> bcc)
{
System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage();
msg.Subject = subject;
msg.Body = body;
msg.From = new System.Net.Mail.MailAddress(from);
msg.IsBodyHtml = true;
if (to != null)
{
foreach (string email in to)
msg.To.Add(email);
}
if (cc != null)
{
foreach (string email in cc)
msg.CC.Add(email);
}
if (bcc != null)
{
foreach (string email in bcc)
msg.Bcc.Add(email);
}
return msg;
}
I've created an example method for you, that will attempt to send an e-mail. Here are a couple of items to note about the implementation:
Settings Class - It holds all of our Client / Server data. Which helps keep the code decoupled for re-usability.
public static void SendNotificationEmail(Settings setting)
{
// Loop through our generic list.
foreach(string email in setting.To)
{
// Assign our email parameters to the message.
MailMessage message = new MailMessage(setting.From, email, setting.Subject, setting.Body);
//Build Our Smtp Client
SmtpClient client = new SmtpClient();
client.Host = setting.SmtpServer;
client.Port = setting.Port;
client.Timeout = setting.Timeout;
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(setting.Username, setting.Password);
}
}
In order to truly help you we would need that Security Model and the precise code that is giving you trouble. Hopefully my implementation might contain a missing piece. This works on my server running the latest version of Internet Information System (IIS).
Also SMTP may fail silently without valid credentials.
Issue was resolved, a higher level configuration and error before the SMTP email was being generated caused the issue.

Why SmtpClient SendAsync throws Exceptions with combinations of UseDefaultCredentials and EnableSsl, while Send works fine

I'm using the System.Net.Mail. There are some topics around this area, but they are ASP.NET and often VB related. I'm isolating the code into a desktop skeleton code, I use .NET 3.5 and C#.
So Send works in all scenario what I tried (EnableSsl false/true, UseDefaultCredentials false/true). But SendAsync only works if UseDefaultCredentials is not set to true (turns out that it can even matter if you explicitly set it to false, it's false by default), EnableSsl is true (OK, that can be server settings too), and hard-code my credentials. I want to be able to SendAsync using UseDefaultCredentials.
Code:
void sendmail() {
MailMessage email = new MailMessage();
email.From = new MailAddress("tcs#software.com");
email.To.Add("tcs#software.com");
email.Subject = "Simple test email subject";
email.Body = "Simple test email body";
string mHost = "mail.software.com";
string mUsername = #"DOMAIN\tcs";
string mPassword = "myfreakinpassword?Really???";
SmtpClient smtpCli = new SmtpClient(mHost);
//smtpCli.UseDefaultCredentials = true;
smtpCli.Credentials = new NetworkCredential(mUsername, mPassword);
smtpCli.EnableSsl = true;
smtpCli.SendCompleted += smtpCli_SendCompleted;
try {
//smtpCli.Send(email);
smtpCli.SendAsync(email, null); // This mofo only works in this combination
}
catch (Exception ex) {
Console.WriteLine(ex);
}
}
void smtpCli_SendCompleted(object sender, AsyncCompletedEventArgs e) {
if (e.Error != null)
Console.WriteLine(e.Error);
}
Use messaging pattern like Prism's EventAggregator (specifying ThreadOption.BackgroundThread). This way the caller sends a message (the message would contain From, To, Subject, Body), and it is asynchronous from the sender's point of view. Then use the synchronous Send function of System.Net.Mail in the consumer/handler of the message.
This probably works because the executing background thread has more proper privileges than the one spawns the SendAsync.

Categories

Resources