Mailkit: [Question] Should you limit instantiations of SmtpClients, or not?

Created on 6 Mar 2020  Â·  13Comments  Â·  Source: jstedfast/MailKit

I've just moved to using MailKit, and I know for a fact that the SmtpClient provided by Microsoft ought to be used as a singleton, in handling multiple mail messages.

Scaling would be done then by issuing X such clients, each with its own message queue.

Do these same rules apply here, or can we aggressively instantiate MailKit's smtpClients on a per message sent basis?

EDIT: On a side note, is there a thread-safe Send method? Because the old SmtpClient was not thread-safe in sending messages.

question

Most helpful comment

Thanks. Fixed.

All 13 comments

It would probably be better to keep a reference to the SmtpClient in order to send multiple messages before disposing it.

When you dispose a MailKit SmtpClient, you are closing the TCP/IP connection with the server (well, technically, the SmtpClient.Disconnect() method does that if you call that before Dispose(), but if not Dispose() will sever the connection for you).

Connecting to a remote host over the internet is an expensive process that is best avoided if possible.

And no, SmtpClient is not thread-safe, but you can lock the SmtpClient.SyncRoot property to synchronize your threads.

@jstedfast Is it okay to cache/use a given SmtpClient instance through the whole application's lifetime?

Yes, but keep in mind that the connection won't remain connected w/o periodic packets being sent.

So you have 2 options:

  1. Keep the connection alive (as best you can) by using SmtpClient.NoOp() every few minutes.
  2. Properly handle exceptions in Send() (e.g. SmtpProtocolException) by re-connecting and trying again.

Either way, you'll want to implement option 2. The question is whether you want to put in the effort for option 1 as well.

Note: while SmtpClient.IsConnected is a useful property to check, it doesn't test the up-to-the-second state of the TCP/IP connection, that state only gets updated in the Connect() method, the Disconnect() method and as a result of an exception being thrown that is a result of the TCP/IP connection being closed by the server.

Your email has been forwarded to Henrico Police as I have unsubscribed long ago from your email chain and still some bad guy is using your email to bully me with unwanted details.

Sorry I had to do this as there is no way for me to stop the bad guy hacking me. Just a FYI.

Sincere Regards
AmudhaUday

Sent from my iPhone

On Mar 6, 2020, at 10:45 AM, SpiritBob notifications@github.com wrote:


Thank you a ton for taking some of your time in completely resolving all my inquiries, @jstedfast !

Rather than locking by using SmtpClient.SyncRoot, I'll be implementing my own synchronization handler, coupled with the Task-Parallel-Library.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.

@jstedfast When I receive the following exception ""An existing connection was forcibly closed by the remote host.", the IsConnected property does not get updated. Although in some exceptions as you've said, it does get updated. Isn't that error tied with the server closing the connection, if so, why wasn't the property updated?

EDIT: Is attempting to do a NoOp operation a good alternative to testing if we have a valid established connection with the SMTP server? (If we get an exception, it means we need to re-establish the connection)

Yes, a NOOP is a great way to check connectivity.

The IsConnected property should get updated, so there's probably a bug. Do you have the StackTrace of the exception? (I'm beginning to think most developers don't know about the System.Exception.StackTrace property). Suuuuuuper useful in tracking these types of bugs down ;-)

@jstedfast Here you go!

{"An existing connection was forcibly closed by the remote host."}
    Data: {System.Collections.ListDictionaryInternal}
    ErrorCode: 10054
    HResult: -2147467259
    HelpLink: null
    InnerException: null
    Message: "An existing connection was forcibly closed by the remote host."
    NativeErrorCode: 10054
    SocketErrorCode: ConnectionReset
    Source: "System.Private.CoreLib"
    StackTrace: "   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()\r\n   at MailKit.Net.NetworkStream.<WriteAsync>d__36.MoveNext()"
    TargetSite: {Void Throw()}

Thank you as always for answering so diligently.

EDIT: Some additional context. After this is thrown, IsConnected stays true, and actually, a connection appears to exist, because attempting to use ConnectAsync yields in an exception, telling me that 'SmtpClient is already connected', which is not true, or if it's true - the connection is clearly invalid.

This forced me to make use of the DisconnectAsync method, to make sure it is able to do a new connection.

Let me know if you need further information, or if you've narrowed down the cause to this.

What SmtpClient method were you calling when you got this? Send()?

What SmtpClient method were you calling when you got this? Send()?

NoOpAsync()

Thanks. Fixed.

Any estimation as to when the changes will be applied to the NuGet package, or they already were?

You can grab the latest build always from the link on the main page (myget). I only publish official builds once every few months or when I have major bugfixes to push.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

spchaplin picture spchaplin  Â·  3Comments

dbogatov picture dbogatov  Â·  7Comments

madu2003 picture madu2003  Â·  7Comments

portal7 picture portal7  Â·  3Comments

syneex picture syneex  Â·  4Comments