SmtpClient发送约280封邮件后报错:无法读取传输连接数据
批量Gmail发送触发
net_io_connectionclosed问题的排查与解决 这问题确实挺挠头的,折腾一周肯定够闹心的!先帮你拆解下核心矛盾:用C#写的批量发件程序,在Windows 2016 VPS搭配SmarterMail 16 SMTP服务器的环境下,发280-300封就触发连接关闭错误,当时连单发都失败,但等1小时又能恢复;更奇怪的是第三方工具用同配置却能正常发。结合你试过的方案,咱们从几个方向深挖:
核心问题分析
- 隐藏的连接/限流限制:你说关了SmarterMail的限流,但可能存在未注意到的细节——比如SMTP并发连接数上限、单IP连接阈值,或是Windows系统层面的TCP端口耗尽(批量发件短时间创建大量连接,TIME_WAIT状态的端口没及时释放,导致新连接建不起来)。第三方工具能发大概率是因为它的连接复用策略更高效,或是用了不同的连接释放逻辑。
- 代码中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的深层设置
- 进入SmarterMail的SMTP服务器配置,找到连接限制板块:调大「最大并发连接数」「单IP最大连接数」「连接超时时间」(比如并发设为100,单IP连接数设为50,超时设为300秒);
- 查看投递限制:确认是否存在「每小时最大发送量」这类细分限流规则,哪怕你全局关了限流,可能某个域或账号的限制没解除;
- 查看SMTP日志:在SmarterMail的日志模块里筛选SMTP相关记录,看服务器断开连接的具体原因(是触发了限制,还是连接异常)。
三、调整Windows系统TCP参数
VPS默认的TCP端口数和超时设置可能不足以支撑批量发件,修改注册表优化:
- 打开注册表编辑器(
regedit),定位到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters; - 添加/修改以下DWORD值(十进制):
TcpTimedWaitDelay:设为30(缩短TIME_WAIT状态端口的超时时间,默认120秒);MaxUserPort:设为65534(允许的最大端口数,默认5000);TcpNumConnections:设为65534(最大TCP连接数);
- 修改后重启服务器生效,这样系统能更快释放闲置端口,避免批量发件时端口耗尽。
四、验证测试
- 先用重构后的代码测试批量发件,看是否还会在280封左右触发报错;
- 若仍报错,查看SmarterMail的SMTP日志定位具体原因;
- 故障发生时用
netstat -ano命令查看TCP连接状态,确认是否存在大量TIME_WAIT或ESTABLISHED连接,判断是否是端口耗尽问题。
内容的提问来源于stack exchange,提问作者Zoinky
相关产品推荐
相关产品推荐

