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

SmtpClient发送约280封邮件后报错:无法读取传输连接数据

批量Gmail发送触发net_io_connectionclosed问题的排查与解决

这问题确实挺挠头的,折腾一周肯定够闹心的!先帮你拆解下核心矛盾:用C#写的批量发件程序,在Windows 2016 VPS搭配SmarterMail 16 SMTP服务器的环境下,发280-300封就触发连接关闭错误,当时连单发都失败,但等1小时又能恢复;更奇怪的是第三方工具用同配置却能正常发。结合你试过的方案,咱们从几个方向深挖:

核心问题分析

  1. 隐藏的连接/限流限制:你说关了SmarterMail的限流,但可能存在未注意到的细节——比如SMTP并发连接数上限、单IP连接阈值,或是Windows系统层面的TCP端口耗尽(批量发件短时间创建大量连接,TIME_WAIT状态的端口没及时释放,导致新连接建不起来)。第三方工具能发大概率是因为它的连接复用策略更高效,或是用了不同的连接释放逻辑。
  2. 代码中SmtpClient的复用问题:你当前的代码是在循环外创建一个SmtpClient复用,但.NET的SmtpClient长期复用连接时,服务器可能会主动断开闲置/高频复用的连接,进而触发报错。

针对性解决方案

一、重构代码的SmtpClient使用逻辑

把SmtpClient的创建移到循环内部,确保每次发件用全新连接并及时释放,同时增加超时设置避免连接挂起:

static void Main(string[] args) {
    Console.WriteLine("How many emails do you want to send?");
    var emailCount = Convert.ToInt32(Console.ReadLine());
    Console.WriteLine("How many seconds do you want to delay between each send");
    var delay = Convert.ToInt32(Console.ReadLine());
    Console.WriteLine("What email address do you want to send to");
    var toEmailAddress = Console.ReadLine();
    Console.WriteLine($"Sending out {emailCount} with {delay} second delay to {toEmailAddress}");

    // 提前读取配置,避免循环内重复IO操作
    var smtpServer = ConfigurationManager.AppSettings["SmtpServer"];
    var userName = ConfigurationManager.AppSettings["Username"];
    var password = ConfigurationManager.AppSettings["Password"];
    var port = Convert.ToInt32(ConfigurationManager.AppSettings["Port"]);
    var fromEmail = ConfigurationManager.AppSettings["FromEmail"];
    var subject = ConfigurationManager.AppSettings["Subject"];

    for (int i = 1; i <= emailCount; i++) {
        // 每次循环创建新的SmtpClient,用using自动释放资源
        using (var client = new SmtpClient(smtpServer)) {
            try {
                var body = "this is a test message";
                client.UseDefaultCredentials = false;
                client.Credentials = new NetworkCredential(userName, password);
                client.Port = port;
                client.Timeout = 10000; // 设置10秒超时,避免连接无响应
                //client.EnableSsl = true;

                MailMessage mailMessage = new MailMessage();
                mailMessage.From = new MailAddress(fromEmail);
                mailMessage.To.Add(toEmailAddress);
                mailMessage.Body = body;
                mailMessage.IsBodyHtml = true;
                mailMessage.Subject = subject;

                client.Send(mailMessage);
                if (delay > 0) {
                    Console.WriteLine("Sleeping...");
                    Thread.Sleep(delay * 1000);
                }
                Console.WriteLine($"Email number {i} was sent successfully.");
            } catch (Exception ex) {
                Console.WriteLine($"Failed to send {i} of {emailCount}");
                Console.WriteLine(ex.ToString());
                // 出错后延迟5秒再重试,避免频繁请求加剧连接压力
                Thread.Sleep(5000);
            }
        }
    }
    Console.WriteLine("Done...press any key to exit");
    Console.ReadKey();
}

二、检查SmarterMail的深层设置

  1. 进入SmarterMail的SMTP服务器配置,找到连接限制板块:调大「最大并发连接数」「单IP最大连接数」「连接超时时间」(比如并发设为100,单IP连接数设为50,超时设为300秒);
  2. 查看投递限制:确认是否存在「每小时最大发送量」这类细分限流规则,哪怕你全局关了限流,可能某个域或账号的限制没解除;
  3. 查看SMTP日志:在SmarterMail的日志模块里筛选SMTP相关记录,看服务器断开连接的具体原因(是触发了限制,还是连接异常)。

三、调整Windows系统TCP参数

VPS默认的TCP端口数和超时设置可能不足以支撑批量发件,修改注册表优化:

  1. 打开注册表编辑器(regedit),定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters;
  2. 添加/修改以下DWORD值(十进制):
    • TcpTimedWaitDelay:设为30(缩短TIME_WAIT状态端口的超时时间,默认120秒);
    • MaxUserPort:设为65534(允许的最大端口数,默认5000);
    • TcpNumConnections:设为65534(最大TCP连接数);
  3. 修改后重启服务器生效,这样系统能更快释放闲置端口,避免批量发件时端口耗尽。

四、验证测试

  1. 先用重构后的代码测试批量发件,看是否还会在280封左右触发报错;
  2. 若仍报错,查看SmarterMail的SMTP日志定位具体原因;
  3. 故障发生时用netstat -ano命令查看TCP连接状态,确认是否存在大量TIME_WAIT或ESTABLISHED连接,判断是否是端口耗尽问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:25:24