Nodemailer出现Connection closed unexpectedly错误求助
问题描述
使用Node.js结合Nodemailer实现邮件发送功能,初期成功发送5-6次邮件,但后续尝试发送时出现以下错误:
Error: Connection closed unexpectedly
at SMTPConnection._onClose (D:\official\SendEmailProject\nodeContactForm\node_modules\nodemailer\lib\smtp-connection\index.js:827:34)
at TLSSocket.SMTPConnection._onSocketClose (D:\official\SendEmailProject\nodeContactForm\node_modules\nodemailer\lib\smtp-connection\index.js:193:42)
at Object.onceWrapper (node:events:628:26)
at TLSSocket.emit (node:events:525:35)
at node:net:757:14
at TCP.done (node:_tls_wrap:584:7) {
code: 'ECONNECTION',
command: 'CONN'
对应的实现代码:
const express = require('express'); const app = express(); const nodemailer = require('nodemailer'); //Middleware app.use(express.static('frontend')); app.use(express.json()); app.get('/',(req,res)=>{ res.sendFile(__dirname + '/frontend/index.html'); }); app.post('/', (req,res)=>{ console.log(req.body); const transporter1 = nodemailer.createTransport({ service:"gmail", host: "smtp.gmail.com", // hostname secure: false, // use SSL port: 587, // port for secure SMTP auth: { user: "my email id", pass: "mypassword" }, tls: { ciphers: "SSLv3", rejectUnauthorized: false, }, }); const mailOptions = { from:req.body.email, to: 'prahlad.hsrao@gmail.com', cc: '24septanjali@gmail.com', subject: 'Form Information', text:"firstName: "+ req.body.fname+"\n" + "lastName"+req.body.lname+"email" + req.body.email+"phone"+req.body.phone+"message"+req.body.message, } transporter1.sendMail(mailOptions,(error,info)=>{ if(error){ console.log(error); res.send('error'); transporter1.close(); } else{ console.log('Email sent' + info.response); res.send('success'); res.redirect('/'); transporter1.close(); } }) }) app.listen(3000,()=>console.log('Server started...'));
问题根源
- 重复创建Transport实例:每次POST请求都新建连接实例,频繁创建销毁触发SMTP服务器连接限制,导致连接被意外关闭。
- Gmail安全策略拦截:直接使用邮箱原密码(未启用应用专用密码),多次请求后触发反滥用机制;两步验证开启后,原密码无法用于SMTP验证。
- 过时的TLS配置:指定
ciphers: "SSLv3"已被Gmail等主流服务器弃用,导致TLS握手失败,连接中断。 - 响应逻辑冲突:
res.send()与res.redirect()同时调用,导致响应发送异常,干扰连接状态。 - 手动关闭连接:主动调用
transporter.close()破坏了Nodemailer的连接池复用机制,加剧连接频繁创建销毁的问题。
修复步骤
1. 全局复用Transport实例
将Transport创建移到请求处理外部,全局只初始化一次,利用连接池复用连接:
const express = require('express'); const app = express(); const nodemailer = require('nodemailer'); // 全局初始化Transport,仅创建一次 const transporter = nodemailer.createTransport({ service:"gmail", host: "smtp.gmail.com", secure: false, port: 587, auth: { user: "你的Gmail邮箱地址", pass: "Gmail应用专用密码" // 替换为应用专用密码 }, tls: { rejectUnauthorized: false, // 保留此配置避免证书验证问题 // 删除ciphers: "SSLv3",使用默认兼容TLS版本 }, }); //Middleware app.use(express.static('frontend')); app.use(express.json()); app.get('/',(req,res)=>{ res.sendFile(__dirname + '/frontend/index.html'); }); app.post('/', (req,res)=>{ console.log(req.body); const mailOptions = { from:req.body.email, to: 'prahlad.hsrao@gmail.com', cc: '24septanjali@gmail.com', subject: 'Form Information', text:`firstName: ${req.body.fname}\nlastName: ${req.body.lname}\nemail: ${req.body.email}\nphone: ${req.body.phone}\nmessage: ${req.body.message}` }; transporter.sendMail(mailOptions,(error,info)=>{ if(error){ console.log(error); res.send('error'); } else{ console.log('Email sent: ' + info.response); res.redirect('/'); // 二选一:要么重定向,要么发送success响应 } // 无需手动关闭连接,Nodemailer自动管理连接池 }) }) app.listen(3000,()=>console.log('Server started...'));
2. 配置Gmail应用专用密码
- 确保Gmail账号已开启两步验证
- 登录Google账号管理页面,进入「安全」-「应用专用密码」,生成对应密码,替换代码中
pass字段的值,禁止使用邮箱原密码。
3. 修正响应逻辑
移除res.send('success')与res.redirect('/')的冲突调用,选择其中一种响应方式即可。
4. 移除手动关闭连接代码
删除所有transporter.close()调用,Nodemailer会自动维护连接池,空闲连接会被自动回收,无需手动干预。
内容的提问来源于stack exchange,提问作者Anjali Patel

