You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# SendMailAsync发送部分小图片附件时连接被强制关闭问题

问题描述

使用SmtpClient的异步方法SendMailAsync发送带小图片附件的邮件时,会触发错误**"An existing connection was forcibly closed by the remote host"**,但其他大小的图片附件能正常发送;改用同步方法Send则可以成功发送这类有问题的附件。附件是基于Byte[]转换的带CID的MemoryStream,代码如下:

public async Task<bool> SendEmail(EmailAccount emailAccount, string[] toemails, string subject, string message, IEnumerable<Attachment> attachments = null)
{
    bool succeed = false;
    try
    {
        using SmtpClient client = new();
        client.DeliveryMethod = SmtpDeliveryMethod.Network;
        client.EnableSsl = true;
        
        client.Host = emailAccount.SMTPHost;
        client.Port = emailAccount.SMTPPort.Value;
        NetworkCredential credentials = new(emailAccount.UserName, emailAccount.UserPassword);
        client.UseDefaultCredentials = false;
        client.Credentials = credentials;
        using MailMessage msg = new();
        msg.From = new MailAddress(emailAccount.UserName);
        foreach (string toemail in toemails)
            msg.To.Add(new MailAddress(toemail));
        msg.Subject = subject;
        msg.IsBodyHtml = true;
        msg.Body = message;
        if (attachments != null)
        {
            foreach(var attachment in attachments)
            {
                msg.Attachments.Add(attachment);
            }
        }

        await client.SendMailAsync(msg);    // 失效
        //client.Send(msg);                 // 正常
        Logger.LogInformation($"Email is sent successfully.");
        succeed = true;
    }
    catch (Exception ex)
    {
        Logger.LogCritical(ex, ex.Message);
    }
    return succeed;
    //return Task.FromResult<bool>(succeed);
}
原因分析
  • 异步流处理的时序冲突:同步方法Send会阻塞主线程,直到整个邮件构建、传输完全完成,MemoryStream和相关资源全程保持有效。但异步方法SendMailAsync是非阻塞的,代码里的MailMessage和SmtpClient都在using块中,要是异步传输还没读完流,using块就提前释放了资源(尤其是附件关联的MemoryStream),SMTP连接中途丢数据,远程服务器就会强制关闭连接。小文件传输速度快,这种时序矛盾更容易触发——大文件传输耗时久,反而大概率能在资源释放前完成传输。

  • 旧版SmtpClient异步实现的缺陷:.NET早期的SendMailAsync没有完全遵循异步最佳实践,内部流管理逻辑和同步版本有差异,对短生命周期的流(比如MemoryStream)兼容性差。同步方法会全程持有流直到传输结束,而异步版本在后台线程处理时,流可能已经被外部的using或者GC回收了。

  • 附件流的生命周期未妥善保持:你添加的Attachment关联的MemoryStream如果没有显式保留引用,异步场景下GC可能提前回收这部分内存。同步场景下主线程一直阻塞,GC不会在传输过程中回收这些资源,所以不会出问题。

内容的提问来源于stack exchange,提问作者jong shin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:32:14