如何用单个端口运行Express HTTPS服务器与TLS WebSocket服务?
单个端口同时承载HTTPS(Express)与WebSocket/自定义TLS服务的方案
核心结论
可以用单个端口实现,但你直接用tls.connect裸连HTTPS服务器的方式不对——HTTPS服务器是TLS封装的HTTP服务器,完成TLS握手后它会等待客户端发送HTTP请求,而你只建立了TLS连接却没发送HTTP数据,所以请求回调自然不会触发。
方案1:用同一个HTTPS服务器同时处理Express与WebSocket(推荐)
WebSocket本身是基于HTTP的升级协议,完全可以直接挂载到已有的HTTPS服务器上,不需要单独开TLS端口。用ws库实现的示例代码:
import express from 'express'; import https from 'https'; import { WebSocketServer } from 'ws'; import { readFileSync } from 'fs'; const app = express(); // 处理Express页面路由 app.get('/', (req, res) => { res.send('<h1>HTTPS页面服务</h1>'); }); // 创建HTTPS服务器 const httpsServer = https.createServer({ key: readFileSync('./server.key'), cert: readFileSync('./server.cert'), }, app); // 挂载WebSocket服务 const wss = new WebSocketServer({ server: httpsServer }); wss.on('connection', (ws) => { console.log('WebSocket客户端连接'); ws.on('message', (data) => { console.log('收到WebSocket消息:', data.toString()); ws.send('已收到消息:' + data.toString()); }); }); httpsServer.listen(3000, () => { console.log('服务器运行在 https://localhost:3000'); });
方案2:同一端口同时处理HTTPS与自定义TLS socket(按需使用)
如果确实需要在同一个端口处理非HTTP的自定义TLS协议,需要自己做协议判断:在TLS握手完成后,根据客户端发送的第一个数据包,判断是HTTP请求还是自定义协议,再分发给不同处理逻辑。示例代码:
import express from 'express'; import https from 'https'; import { createServer as createTlsServer } from 'node:tls'; import { readFileSync } from 'fs'; const app = express(); app.get('/', (req, res) => res.send('HTTPS响应')); const tlsServer = createTlsServer({ key: readFileSync('./server.key'), cert: readFileSync('./server.cert'), }, (socket) => { // 捕获第一个数据包判断协议类型 socket.once('data', (chunk) => { // 检查是否是HTTP请求(以GET/POST等方法开头) const isHttpRequest = /^[A-Z]+\s/.test(chunk.toString()); if (isHttpRequest) { // 交给HTTPS服务器处理,把第一个数据包重新塞回socket https.createServer({}, app).emit('connection', socket); socket.unshift(chunk); } else { // 处理自定义TLS socket逻辑 console.log('自定义TLS连接建立'); socket.on('data', (data) => { console.log('收到自定义数据:', data.toString()); socket.write('自定义响应:' + data.toString()); }); } }); }); tlsServer.listen(3000, () => { console.log('多协议TLS服务器运行在端口3000'); });
关于你测试代码的问题解决
如果你非要用tls.connect连接HTTPS端口并触发请求回调,需要手动发送HTTP请求:
import { connect } from 'node:tls'; const socket = connect({ host: process.env.RELAY_SERVER_HOST_ORIGIN || 'localhost', port: 3000, rejectUnauthorized: false, // 本地测试用,生产环境请勿开启 }); socket.on('connect', () => { // 发送HTTP GET请求,触发服务器端的req回调 socket.write('GET / HTTP/1.1\r\nHost: localhost:3000\r\nConnection: close\r\n\r\n'); }); socket.on('data', (data) => { console.log('服务器响应:', data.toString()); });
内容的提问来源于stack exchange,提问作者qnilab
相关产品推荐
相关产品推荐

