SmtpClient超时不起作用

10

我已经设置了SmtpClient类的Timeout属性,但它似乎不起作用。当我给它一个1毫秒的值时,在代码执行时超时实际上是15秒。我从msdn中获取了代码。

string to = "jane@contoso.com";
string from = "ben@contoso.com";
string subject = "Using the new SMTP client.";
string body = @"Using this new feature, you can send an e-mail message from an application very easily.";
MailMessage message = new MailMessage(from, to, subject, body);
SmtpClient client = new SmtpClient("1.2.3.4");
Console.WriteLine("Changing time out from {0} to 100.", client.Timeout);
client.Timeout = 1;
// Credentials are necessary if the server requires the client 
// to authenticate before it will send e-mail on the client's behalf.
client.Credentials = CredentialCache.DefaultNetworkCredentials;
client.Send(message);

我在mono上尝试了这个实现,但它也没有起作用。

有人遇到了同样的问题吗?

2个回答

16

复现您的测试 - 我这里可以正常工作

您询问是否有人遇到同样的问题 - 我刚试了一下您的代码在Windows 7、VS 2008和.NET 2.0上 - 它没问题。当超时设置为1,与您的代码一样,我几乎立即收到以下错误信息:

Unhandled Exception: System.Net.Mail.SmtpException: The operation has timed out
   at System.Net.Mail.SmtpClient.Send(MailMessage message)
   at mailtimeout.Program.Main(String[] args) in c:\test\mailtimeout\Program.cs:line 29

我认为问题可能是你对超时的期望有所不同。超时意味着连接已经成功建立,但没有从服务器返回响应。这意味着你需要在目标地点实际上有一个正在监听25端口的服务器,但它没有响应。为了进行这个测试,我使用Tcl在25上创建了一个什么也不做的套接字:

c:\> tclsh
% socket -server foo 25

当我将超时时间更改为15000时,直到5秒后我才收到超时错误。

如果连接无法建立,为什么Smtp.Timeout没有效果

如果在25端口没有正在监听,或者主机无法访问,则要等到至少20秒后才会发生超时,即system.net.tcpclient层超时。 这是低于system.net.mail层的。从一篇解释这个问题并提供解决方案的优秀文章中可以了解到:

您将注意到,两个类System.Net.Sockets.TcpClient和System.Net.Sockets.Socket都没有用于连接套接字的超时。我的意思是可以设置的超时。调用Connect / BeginConnect方法时,.NET Sockets不提供连接超时,同时建立同步/异步套接字连接。相反,如果尝试连接的服务器未在侦听,或者存在任何网络错误,则强制连接等待很长时间才会抛出异常。默认超时为20-30秒

邮件没有更改超时的能力(这是有道理的,邮件服务器通常正常运行),实际上从system.net.socket也没有更改连接的能力,这真的很令人惊讶。但是您可以进行异步连接,并且然后可以确定主机是否正在运行并且端口已打开。从此MSDN线程中以及特别是此帖子中,以下代码有效:

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = socket.BeginConnect("192.168.1.180", 25, null, null);
// Two second timeout
bool success = result.AsyncWaitHandle.WaitOne(2000, true);
if (!success) {
    socket.Close();
    throw new ApplicationException("Failed to connect server.");
}

但是,如果您尝试连接一个未在25端口上监听的IP地址,或者该IP地址没有被任何人使用,那么超时会在您的计算机上起作用吗? - croisharp
我尝试使用你说的方法打开一个没有作用的套接字,但是当我在命令行中按下^C键关闭套接字时,它会抛出操作超时异常。 - croisharp
当没有主机在监听时:是的,我也会遇到20秒的超时。这是在TCP层。我扩展了我的答案来解决这个问题。 - ckhan
非常感谢,我正在构建一个网站,让每个人都可以向他们的帐户添加SMTP服务器并发送消息。由于+1请求,可能会对性能产生影响,但这比20秒的延迟要好。我认为理想的解决方案是使用TcpClient类构建自己的SmtpClient。 - croisharp
如果成功了,我们该怎么做呢?也就是说,我们如何通过这个套接字发送消息? - mcmillab
显示剩余2条评论

2

在ckhan的回答基础上,我想和大家分享一个实现更短超时时间的建议:

var task = Task.Factory.StartNew(() => SendEmail(email));

if (!task.Wait(6000))
   // error handling for timeout on TCP layer (but you don't get the exception object)

然后在SendEmail()函数中:

using (var client = new SmtpClient(_serverCfg.Host, _serverCfg.Port)) 
{        
    try
    {
        client.Timeout = 5000;   // shorter timeout than the task.Wait()
        // ...
        client.Send(msg);
    }
    catch (Exception ex)
    {
        // exception handling
    }
}

这个解决方案的取舍是在task.Wait情况下你无法获取异常细节,但也许这是值得的?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接