Node.js监听用户输入时Ctrl-C无法关闭ChromeDriver问题排查
问题原因分析
当开启stdin.rawMode = true后,Node.js会禁用默认的SIGINT(Ctrl-C)信号处理机制,此时Ctrl-C会被解析为ASCII字符\x03而非触发系统信号。如果你的输入监听逻辑没有专门处理这个字符,或者在处理前主进程就提前退出,就会导致ChromeDriver的清理逻辑未执行,进程残留。而键是你明确在输入处理中执行了完整的清理(关闭ChromeDriver)再退出,所以能正常工作。
解决方法
1. 手动监听SIGINT信号
直接注册SIGINT信号的处理函数,在其中执行跳过部分清理的退出逻辑,确保ChromeDriver被关闭:
process.on('SIGINT', async () => { console.log('收到Ctrl-C,执行精简清理退出'); // 跳过非必要清理步骤,仅关闭ChromeDriver try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } process.exit(0); });
2. 处理输入中的Ctrl-C字符(\x03)
在stdin的data事件监听中,捕获\x03字符并执行和SIGINT相同的逻辑:
process.stdin.on('data', async (data) => { const input = data.toString().trim(); if (input.toLowerCase() === 'q') { console.log('收到Q键,执行完整清理退出'); // 执行完整清理步骤 try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } process.exit(0); } else if (input === '\x03') { // 捕获Ctrl-C对应的字符 console.log('收到Ctrl-C输入,执行精简清理退出'); try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } process.exit(0); } });
3. 确保异步清理完成后再退出
注意driver.quit()是异步操作,必须等待它完成后再调用process.exit(),否则主进程提前退出会导致ChromeDriver进程残留。
复现Ctrl-C默认行为的替代方式
如果需要在监听用户输入时保留Ctrl-C的默认终止行为(同时执行自定义逻辑),可以这样做:
let isExiting = false; process.on('SIGINT', async () => { if (isExiting) return; isExiting = true; // 执行自定义精简清理逻辑 try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } // 触发默认的SIGINT行为 process.kill(process.pid, 'SIGINT'); });
或者在rawMode下捕获\x03后,恢复默认信号处理再发送SIGINT:
process.stdin.on('data', async (data) => { if (data.toString() === '\x03') { // 执行自定义清理 try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } // 恢复默认stdin模式和信号处理 process.stdin.setRawMode(false); process.stdin.pause(); // 发送SIGINT触发默认退出 process.kill(process.pid, 'SIGINT'); } });
可复现代码(修改前)
const { Builder } = require('selenium-webdriver'); const chrome = require('selenium-webdriver/chrome'); async function startApp() { const driver = await new Builder() .forBrowser('chrome') .setChromeOptions(new chrome.Options().headless()) .build(); console.log('应用启动,按Q退出,按Ctrl-C强制退出'); process.stdin.setRawMode(true); process.stdin.resume(); process.stdin.on('data', async (data) => { const input = data.toString().trim(); if (input.toLowerCase() === 'q') { console.log('正在清理退出...'); await driver.quit(); process.exit(0); } }); // 原代码未处理SIGINT } startApp().catch(err => console.error('启动失败:', err));
修改后的完整代码
const { Builder } = require('selenium-webdriver'); const chrome = require('selenium-webdriver/chrome'); async function startApp() { let driver; try { driver = await new Builder() .forBrowser('chrome') .setChromeOptions(new chrome.Options().headless()) .build(); console.log('应用启动,按Q退出,按Ctrl-C强制退出'); process.stdin.setRawMode(true); process.stdin.resume(); // 处理输入事件 process.stdin.on('data', async (data) => { const input = data.toString(); if (input.toLowerCase().trim() === 'q') { await handleExit(true, driver); } else if (input === '\x03') { // 捕获Ctrl-C输入 await handleExit(false, driver); } }); // 监听SIGINT信号 process.on('SIGINT', async () => { await handleExit(false, driver); }); } catch (err) { console.error('启动失败:', err); process.exit(1); } } // 统一退出处理函数 async function handleExit(fullCleanup, driver) { console.log(fullCleanup ? '执行完整清理退出...' : '执行精简清理退出...'); if (driver) { try { await driver.quit(); } catch (err) { console.error('关闭ChromeDriver失败:', err); } } // 可选:如果是fullCleanup,执行其他清理步骤(比如关闭文件、释放资源) if (fullCleanup) { // 这里添加额外的清理逻辑 } process.exit(0); } startApp();
内容的提问来源于stack exchange,提问作者slhck
相关产品推荐
相关产品推荐

