【问题标题】:Connecting to smtp.live.com with the TcpClient class使用 TcpClient 类连接到 smtp.live.com
【发布时间】:2011-09-10 07:59:11
【问题描述】:

我正在尝试使用 TcpClient 类连接到 smtp.live.com。这里有一个很好的连接到 Gmail 的例子:Testing SMTP server is running via C#

不幸的是,当更新它以使用 smtp.live.com 时,当调用 AuthenticateAsClient 方法时,我收到“IOException: The handshake failed due to an unexpected packet format”。

我该如何解决这个问题?

class Program
{
    static void Main(string[] args)
    {
        using (var client = new TcpClient())
        {
            var server = "smtp.live.com";
            var port = 25;
            client.Connect(server, port);
            using (var stream = client.GetStream())
            using (var sslStream = new SslStream(stream))
            {
                // Getting an IOException here
                sslStream.AuthenticateAsClient(server);
                using (var writer = new StreamWriter(sslStream))
                using (var reader = new StreamReader(sslStream))
                {
                    writer.WriteLine("EHLO " + server);
                    writer.Flush();
                    Console.WriteLine(reader.ReadLine());
                }
            }
        }
        Console.WriteLine("Press Enter to exit...");
        Console.ReadLine();
    }
}

我尝试在 AuthenticateAsClient 中指定 SslProtocol。 Tls 或 Ssl3 都不起作用。

还尝试为 RemoteCertificateValidation 提供回调,该回调始终返回 true,以防服务器证书无效。那也没用。

注意:请不要建议我使用 SmtpClient;我需要比它提供的更多的控制权。

【问题讨论】:

  • 你需要什么样的控制,SmtpClient 不提供?
  • 我正在创建一个 SMTP 代理,它将支持来自我们内部使用的一些不支持 TLS/SSL 的程序的未加密连接。 SMTP 代理服务器将接受来自这些程序的连接并将信息转发到需要 TLS/SSL 的 smtp.live.com 服务器。
  • 为什么不能使用 SmtpClient 进行出站连接?在我看来,只有代理中的入站连接需要自定义。
  • @jgauffin - 如果我使用 SmtpClient,我需要完全接收和拆除电子邮件。这可能包括多个收件人、多个抄送、多个附件等。工作量太大了。

标签: c# .net smtp


【解决方案1】:

感谢 nos 让我走上正轨。 smtp.live.com 服务器需要以下事件序列:

  1. 连接
  2. HELO - 在发送之前不会接受 STARTTLS
  3. STARTTLS - 显然这会将服务器设置为接受加密连接
  4. SslStream.AuthenticateAsClient() - 这似乎让 C# 框架和 SMTP 服务器达成“理解”:)
  5. 现在我们有了加密连接,通常的 SMTP 命令就可以工作了

无论如何,此代码适用于端口 587 上的 smtp.live.com 和 smtp.gmail.com:

class Program
{
    static void Main(string[] args)
    {
        const string server = "smtp.live.com";
        const int port = 587;
        using (var client = new TcpClient(server, port))
        {
            using (var stream = client.GetStream())
            using (var clearTextReader = new StreamReader(stream))
            using (var clearTextWriter = new StreamWriter(stream) { AutoFlush = true })
            using (var sslStream = new SslStream(stream))
            {
                var connectResponse = clearTextReader.ReadLine();
                if (!connectResponse.StartsWith("220"))
                    throw new InvalidOperationException("SMTP Server did not respond to connection request");

                clearTextWriter.WriteLine("HELO");
                var helloResponse = clearTextReader.ReadLine();
                if (!helloResponse.StartsWith("250"))
                    throw new InvalidOperationException("SMTP Server did not respond to HELO request");

                clearTextWriter.WriteLine("STARTTLS");
                var startTlsResponse = clearTextReader.ReadLine();
                if (!startTlsResponse.StartsWith("220"))
                    throw new InvalidOperationException("SMTP Server did not respond to STARTTLS request");

                sslStream.AuthenticateAsClient(server);

                using (var reader = new StreamReader(sslStream))
                using (var writer = new StreamWriter(sslStream) { AutoFlush = true })
                {
                    writer.WriteLine("EHLO " + server);
                    Console.WriteLine(reader.ReadLine());
                }
            }
        }
        Console.WriteLine("Press Enter to exit...");
        Console.ReadLine();
    }
}

【讨论】:

  • @eFloh - 感谢您的提醒。所以不会让我马上这样做...不得不等待 24 小时或类似的时间。
  • 我在 CodePlex 上用简单的SMTP Proxy 创建了一个项目。希望它可以帮助其他人摆脱这个泡菜。
  • 任何不使用 SSL 的示例,用于连接到 Intranet 中的 Exchange 服务器?
  • 发送EHLO而不是HELO通常会得到多行响应,显示服务器支持的所有命令,每行以250开头。如果要使用EHLO,则需要循环调用clearTextReader.ReadLine(),直到响应的最后一行以250 开头(包括响应代码后的空格)。包含更多行的 SMTP 响应以 250- 开头,而最后一行以 250 开头(包括空格)。
【解决方案2】:

如果你想在连接上使用 ssl。您应该使用端口 465 用于 ssl 或 587 用于 tls。

提示:先尝试不使用 ssl,等一切正常后再添加。

链接:http://www.checktls.com/tests.html 查看一些 starttls 示例。

【讨论】:

  • 或者通过检查smtp服务器是否支持StartTLS来协商TLS。
  • 你说得对……我试过 465 和 587。不行。
  • @nos - 感谢您的提示。我想我找到了解决办法。
猜你喜欢
  • 1970-01-01
  • 2016-01-07
  • 1970-01-01
  • 2015-08-16
  • 1970-01-01
  • 2012-11-12
  • 1970-01-01
  • 2016-04-08
  • 2015-03-27
相关资源
最近更新 更多