MailKit SMTP slow when sending messages - c#

I've been using MailKit to retrieve some emails using IMAP and forwarding them using SMTP (more info here), but it takes really long for the SMTP to send the email.
I'm using mailkit via NuGet
This is the code I'm using
<!-- language: c# -->
var before = DateTime.Now;
Console.Write("\tForwarding email... ");
smtpClient.Send(forwardMessage, fromMailboxAddress, new[] { toMailboxAddress });
Console.WriteLine(" done! ({0})", DateTime.Now - before);
And the time it takes is usually more than 30s. What is making me suspect there's somethign wrong is that the email is actually forwarded almost instantly: few seconds (or even less) than the code reaches the smtpClient.Send method, I can see the message appearing in the destination email account (I have Thunderbird opened at the same time), but something makes the code to be still doing something in the Send code line.
Is there a way to know what the code is doing and why does it take so long?

You can see what the SmtpClient.Send() code is doing here: SmtpClient.cs:1543
I can't think of any reason it would take more than 30 seconds to send if you are seeing the message show up at the destination within seconds after client.Send() is being called.
My only guess is that the server is taking a long time to send a response to the DATA (or BDAT) command (which is the command that actually sends the raw message data).
In other words, my guess is that it would have to be this line: SmtpClient.cs:1517 or this line: SmtpClient.cs:1488
This is the ReadResponse() method: SmtpClient.cs:320. Most likely the waiting will be in the Poll() call or, if the stream does not support polling (SslStream), then it will be stuck in the stream.Read() call waiting for a response from the server.

Related

MSMQ via C# - ACK that message received?

I'm sending a message to a private queue via c# :
MessageQueue msgQ = new MessageQueue(#".\private$\aaa");
msgQ.Formatter = new XmlMessageFormatter(new[] { typeof (String) });
msgQ.Send(msg);
It does work and I do see the message in the queue.
However, is there any way to get an ACK whether the message got to the queue with success ?
ps
BeginPeek and PeekCompleted is an event which is raised when a message becomes available in the queue or when the specified interval of time has expired. it is not helping me because I need to know if the message that I sent was received by msmq. BeginPeek will be raised also if someone else entered a message to the queue. and the last thing I want is to check via BeginPeek - from who this message comes from.
How can I do that?
ps2
Or maybe I don't have to worry since msgQ.Send(msg); will raise an exception if a message wasn't inserted....?
I think what you are trying to do should not be handled in code. When you send the message, it is placed in the outgoing queue. There are numerous reasons why it would not reach the destination, such as a network partition or the destination queue being full. But this should not matter to your application - as far as it is concerned, it sent the message, it committed transaction, it received no error. It is a responsibility of the underlying infrastructure to do the rest, and that infrastructure should be monitored to make sure there are no technical issues.
Now what should really be important to your application is the delivery guarantees. I assume from the scenario that you are describing that you need durable transactional queues to ensure that the message is not lost. More about the options available can be read here
Also, if you need some identifier to display to the user as a confirmation, a common practice is to generate it in the sending code and place it in the message itself. Then the handling code would use the id to do the required work.
Using transactional queues and having all your machines enroll in DTC transactions likely would provide what you're looking for. However, it's kinda a pain in the butt and DTC has side effects - like all transactions are enrolled together, including DB transactions.
Perhaps a better solution would to be use a framework like MassTransit or NServiceBus and do a request-response, allowing the reviecer to respond with actual confirmation message say not only "this has been delivered" but also "I acknowledge this" with timeout options.
As Oleksii have explained about reliable delivery.
However this can effect on performance.
What I can suggest is:
Why not create a MSMQ server on the machine that is sending MSG to other system.
What I am thinking is
Server 1 sends MSMSQ to Server 2
Server 2 receives adds to queue
Server 2 process queue/fire your code here to send a MSMQ msg to Server 1
Server 1 receives MSG (any successful msg with MSGId)
Do your further task
This approach can be an extra mile, but will keep your servers out of performance Lag.

Trying to send smtp email in C#. No errors but does not get received. PHP works

I have a server that runs IIS and PHP. I have numerous webpages that send emails, some to me, some to the users. This all works great. I am on a Comcast Business Class account which means I can use smtp.comcast.net as my SMTP server, use port 25, and not use any sort of authentication which is great. And it works just fine.
Now fast forward to today. I am writing some custom C# code to monitor a folder structure and basically email me the new file if it matches certain parameters. In my C# code, I try to use the same settings, but it doesn't work. The SmtpClient.Send() function does not throw an Exception and my code completes the routine as if everything is happy and working. But then I wait and wait and wait, and I never receive the email.
SmtpClient smtp = new SmtpClient("smtp.comcast.net");
smtp.Port = 25;
smtp.EnableSsl = false;
smtp.UseDefaultCredentials = false;
smtp.Timeout = 2500;
smtp.Send(mail);
onStatusUpdate("Successfully sent email to " + mail.To + (mail.CC.Count > 0 ? " and CC'd " + mail.CC.ToString() : ""));
The "mail" object is of type MailMessage and is setup with the To, From, subject, body, and CC. Also has HTML and Plain Text alternate views.
I guess the easiest question, is if there is a trick to sending email the oldschool port-25 way in C# that doesn't exist in PHP?
And the only reason I mention PHP is because I know my firewall isn't blocking port 25, I know my ISP has it open, I know I have the right server, I know it should work.
I don't know if this affects their Business Class accounts, but Comcast just recently (<2 months ago) closed off port 25 for all their email accounts. try using port 587
http://customer.comcast.com/help-and-support/internet/email-client-programs-with-xfinity-email/
The first place to look is in the mail server logs of the outgoing mail server that you are using to send this message. These should tell you whether or not the mail server is even receiving the message from your C# program for queuing, and if so - what's happening when it attempts to deliver the message to the remote MTA.
Well about 5 hours after I started testing, I got all my test emails at once including the embedded HTML and everything else. So it is working just as it should. I guess since the signature was slightly different they block them until they are deemed non-spam. It also appears that now when I send an email it goes through instantly.
So Comcast has some sort of time delay filter apparently for anybody else in the future with this problem.
I ended up adding mails to the (to list),
and put sending in foreach for every mail I send mail separately,
and it worked!

Let me know when mail was NOT received to the mail (for successful DB backup for example)

I have a site I'm building and have a DB backup running on it several times a day.
Every successful backup is sent to my mail (and directed by a rule to a folder since there are many backups..) and every failure is also sent to me (not directed by a rule).
I'm afraid that the task will stop running from some reason and I will not know (no alerts arriving but how can I notice it in the blur of mails).
Is there a SW or process that alerts me when a mail was not received during a specific time?
The reason I'm asking here is that I want to develop this kind of thing (if does not exist).
Thanks
You cannot know, unless you create something for yourself. E.g. a rule to auto-reply the received message (at receiver's mailbox). Your program (that sent the e-mail) should check his own mailbox within x seconds for the reply after sending.
Normally it is just a send and goodbye system, unless the e-mail box is full, unreachable, etc.

How to wait until stream write is done

I have a server app that listens for connections on port 8888. I am also creating the client application. It is a simple application the only hard thing about it is managing multiple connections. so I just need to send files between computers so the way I do that, I don't know if it is wright but it works maybe you guys can correct me. here is my algorithm when sending the file:
NetworkStream stream = \\ initialize it
while(someCondition)
{
// first I open the file for reading and read chunks of it
byte[] chunk = fileRead(file, indexStart, indexEnd) // I have a similar method this is just to illustate my point
stream.Write(chunk, \\other params)
// since I often send large files it will be nice if I can wait here
// until the stream.Write is done. when debuging this the while loop
// executes several times then it waits.
}
and on the other side I read bytes from that stream and write it to a file.
I also need to wait sometimes because I send multiple files and I want to make sure that the first file has been sent before moving to the next. I know I can solve this by using the stream.Read method once the transfer has been done. and sending data back from the client. but sometimes I believe it will be helpful to know when the stream.write is done.
Edit
ok so based on your answers I can for example send to the client the number of bytes that I am planing to send. and once the client recives that many bytes it means it is done. But my question is if this is efficient. I mean doing something like
on the server:
writing data "sending the file length"
read data "check to see if the client received the length" (expecting a string ok for example)
write data "tel the client the name of the file"
read data "check to see if the client recived the name of the file"
write data "start sending chuncks of the file"
read data "wait until client replies with the string ok for example"
The write is complete when the line
stream.Write(chunk, \\other params)
completes. It's worth noting that this does not imply that the other end has received anything. In fact, immediately subsequent to that line, the data is likely to be in some buffer on the sending machine. That means that it's now out of your control. If you want receipt confirmation, the remote end will have to let you know.
Stream.Write is synchronous, so it will always block your thread until the writing finishes.

NetworkStream.Write returns immediately - how can I tell when it has finished sending data?

Despite the documentation, NetworkStream.Write does not appear to wait until the data has been sent. Instead, it waits until the data has been copied to a buffer and then returns. That buffer is transmitted in the background.
This is the code I have at the moment. Whether I use ns.Write or ns.BeginWrite doesn't matter - both return immediately. The EndWrite also returns immediately (which makes sense since it is writing to the send buffer, not writing to the network).
bool done;
void SendData(TcpClient tcp, byte[] data)
{
NetworkStream ns = tcp.GetStream();
done = false;
ns.BeginWrite(bytWriteBuffer, 0, data.Length, myWriteCallBack, ns);
while (done == false) Thread.Sleep(10);
}
 
public void myWriteCallBack(IAsyncResult ar)
{
NetworkStream ns = (NetworkStream)ar.AsyncState;
ns.EndWrite(ar);
done = true;
}
How can I tell when the data has actually been sent to the client?
I want to wait for 10 seconds(for example) for a response from the server after sending my data otherwise I'll assume something was wrong. If it takes 15 seconds to send my data, then it will always timeout since I can only start counting from when NetworkStream.Write returns - which is before the data has been sent. I want to start counting 10 seconds from when the data has left my network card.
The amount of data and the time to send it could vary - it could take 1 second to send it, it could take 10 seconds to send it, it could take a minute to send it. The server does send an response when it has received the data (it's a smtp server), but I don't want to wait forever if my data was malformed and the response will never come, which is why I need to know if I'm waiting for the data to be sent, or if I'm waiting for the server to respond.
I might want to show the status to the user - I'd like to show "sending data to server", and "waiting for response from server" - how could I do that?
I'm not a C# programmer, but the way you've asked this question is slightly misleading. The only way to know when your data has been "received", for any useful definition of "received", is to have a specific acknowledgment message in your protocol which indicates the data has been fully processed.
The data does not "leave" your network card, exactly. The best way to think of your program's relationship to the network is:
your program -> lots of confusing stuff -> the peer program
A list of things that might be in the "lots of confusing stuff":
the CLR
the operating system kernel
a virtualized network interface
a switch
a software firewall
a hardware firewall
a router performing network address translation
a router on the peer's end performing network address translation
So, if you are on a virtual machine, which is hosted under a different operating system, that has a software firewall which is controlling the virtual machine's network behavior - when has the data "really" left your network card? Even in the best case scenario, many of these components may drop a packet, which your network card will need to re-transmit. Has it "left" your network card when the first (unsuccessful) attempt has been made? Most networking APIs would say no, it hasn't been "sent" until the other end has sent a TCP acknowledgement.
That said, the documentation for NetworkStream.Write seems to indicate that it will not return until it has at least initiated the 'send' operation:
The Write method blocks until the requested number of bytes is sent or a SocketException is thrown.
Of course, "is sent" is somewhat vague for the reasons I gave above. There's also the possibility that the data will be "really" sent by your program and received by the peer program, but the peer will crash or otherwise not actually process the data. So you should do a Write followed by a Read of a message that will only be emitted by your peer when it has actually processed the message.
TCP is a "reliable" protocol, which means the data will be received at the other end if there are no socket errors. I have seen numerous efforts at second-guessing TCP with a higher level application confirmation, but IMHO this is usually a waste of time and bandwidth.
Typically the problem you describe is handled through normal client/server design, which in its simplest form goes like this...
The client sends a request to the server and does a blocking read on the socket waiting for some kind of response. If there is a problem with the TCP connection then that read will abort. The client should also use a timeout to detect any non-network related issue with the server. If the request fails or times out then the client can retry, report an error, etc.
Once the server has processed the request and sent the response it usually no longer cares what happens - even if the socket goes away during the transaction - because it is up to the client to initiate any further interaction. Personally, I find it very comforting to be the server. :-)
In general, I would recommend sending an acknowledgment from the client anyway. That way you can be 100% sure the data was received, and received correctly.
If I had to guess, the NetworkStream considers the data to have been sent once it hands the buffer off to the Windows Socket. So, I'm not sure there's a way to accomplish what you want via TcpClient.
I can not think of a scenario where NetworkStream.Write wouldn't send the data to the server as soon as possible. Barring massive network congestion or disconnection, it should end up on the other end within a reasonable time. Is it possible that you have a protocol issue? For instance, with HTTP the request headers must end with a blank line, and the server will not send any response until one occurs -- does the protocol in use have a similar end-of-message characteristic?
Here's some cleaner code than your original version, removing the delegate, field, and Thread.Sleep. It preforms the exact same way functionally.
void SendData(TcpClient tcp, byte[] data) {
NetworkStream ns = tcp.GetStream();
// BUG?: should bytWriteBuffer == data?
IAsyncResult r = ns.BeginWrite(bytWriteBuffer, 0, data.Length, null, null);
r.AsyncWaitHandle.WaitOne();
ns.EndWrite(r);
}
Looks like the question was modified while I wrote the above. The .WaitOne() may help your timeout issue. It can be passed a timeout parameter. This is a lazy wait -- the thread will not be scheduled again until the result is finished, or the timeout expires.
I try to understand the intent of .NET NetworkStream designers, and they must design it this way. After Write, the data to send are no longer handled by .NET. Therefore, it is reasonable that Write returns immediately (and the data will be sent out from NIC some time soon).
So in your application design, you should follow this pattern other than trying to make it working your way. For example, use a longer time out before received any data from the NetworkStream can compensate the time consumed before your command leaving the NIC.
In all, it is bad practice to hard code a timeout value inside source files. If the timeout value is configurable at runtime, everything should work fine.
How about using the Flush() method.
ns.Flush()
That should ensure the data is written before continuing.
Bellow .net is windows sockets which use TCP.
TCP uses ACK packets to notify the sender the data has been transferred successfully.
So the sender machine knows when data has been transferred but there is no way (that I am aware of) to get that information in .net.
edit:
Just an idea, never tried:
Write() blocks only if sockets buffer is full. So if we lower that buffers size (SendBufferSize) to a very low value (8? 1? 0?) we may get what we want :)
Perhaps try setting
tcp.NoDelay = true

Categories

Resources