跨设备访问Socket.io聊天应用遇net::ERR_CONNECTION_REFUSED错误求助
Socket.io跨设备连接失败及CORS问题排查解决
问题概述
测试聊天应用在其他设备无法访问,连接时出现ERR_CONNECTION_REFUSED错误;尝试ngrok、localtunnel端口转发后问题依旧;同时存在strict-origin-when-cross-origin问题,即使已将CORS的origin设为*。使用Socket.io版本4.7.2。
错误信息
localhost:3000/socket.io/?EIO=4&transport=polling&t=Oi2Ko0C:1 Failed to load resource: net::ERR_CONNECTION_REFUSED localhost:3000/socket.io/?EIO=4&transport=polling&t=Oi2KorJ:1
服务端代码
const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const mongoose = require('mongoose'); const session = require('express-session'); const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; const bcrypt = require('bcryptjs'); const app = express(); const server = http.createServer(app); const io = socketIo(server, { cors: { origin: "*", methods: ["GET", "POST"] }, transports: ['websocket'] }); mongoose.connect('notgonnagiveyouthat', { useNewUrlParser: true, useUnifiedTopology: true }); const PORT = 3000; const userSchema = new mongoose.Schema({ username: String, password: String }); const User = mongoose.model('User', userSchema); const messageSchema = new mongoose.Schema({ username: String, content: String, timestamp: { type: Date, default: Date.now } }); const Message = mongoose.model('Message', messageSchema); passport.use(new LocalStrategy(async (username, password, done) => { try { const user = await User.findOne({ username: username }); if (!user) { return done(null, false, { message: 'Username not registered' }); } const isMatch = await bcrypt.compare(password, user.password); if (isMatch) { return done(null, user); } else { return done(null, false, { message: 'Incorrect password' }); } } catch (err) { return done(err); } })); passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser(async (id, done) => { try { const user = await User.findById(id); done(null, user); } catch (err) { done(err); } }); app.use(session({ secret: 'secret', resave: true, saveUninitialized: true })); app.use(passport.initialize()); app.use(passport.session()); app.use(express.urlencoded({ extended: false })); app.get('/login', (req, res) => { res.sendFile(__dirname + '/login.html'); }); app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })); app.get('/signup', (req, res) => { res.sendFile(__dirname + '/signup.html'); }); app.post('/signup', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username }); if (user) { return res.redirect('/signup'); } const hashedPassword = await bcrypt.hash(password, 10); const newUser = new User({ username, password: hashedPassword }); await newUser.save(); res.redirect('/login'); }); app.get('/', (req, res) => { if (!req.isAuthenticated()) { return res.redirect('/login'); } res.sendFile(__dirname + '/index.html'); }); io.on('connection', async (socket) => { console.log('User connected'); try { const messages = await Message.find().sort({ timestamp: -1 }).limit(10).exec(); socket.emit('previousMessages', messages.reverse()); } catch (err) { console.error("Error fetching messages:", err); } socket.on('sendMessage', async (data) => { const message = new Message({ username: data.username, content: data.content }); try { await message.save(); io.emit('receiveMessage', message); } catch (err) { console.error("Error saving message:", err); } }); socket.on('disconnect', () => { console.log('User disconnected'); }); }); server.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
解决方案
1. 修正客户端连接地址
其他设备无法通过localhost:3000访问你的服务,必须替换为:
- 局域网内:你的电脑局域网IP(如
192.168.x.x:3000) - 公网环境:ngrok/localtunnel提供的转发地址(如
https://abc123.ngrok.io)
确保客户端代码中Socket.io的连接地址使用上述正确地址,而非localhost。
2. 调整服务器监听地址
修改server.listen代码,让服务器监听所有网络接口,而非仅本地:
server.listen(PORT, '0.0.0.0', () => { console.log(`Server is running on port ${PORT}`); });
3. 完善CORS配置
当前CORS配置缺少必要的头部和凭证支持,尤其是结合Passport/session使用时,需补充配置:
const io = socketIo(server, { cors: { origin: "*", // 若需带凭证,需改为具体域名/IP,如"https://abc123.ngrok.io"或"http://192.168.x.x:3000" methods: ["GET", "POST"], allowedHeaders: ["*"], credentials: true // 开启凭证支持,需配合前端withCredentials: true }, // 建议移除transports强制限制,让Socket.io自动选择合适的传输方式 // transports: ['websocket'] });
注意:当credentials: true时,origin不能设为*,需指定具体的允许来源。
4. 检查端口转发与防火墙
- 确保ngrok/localtunnel正确映射端口:执行
ngrok http 3000后,使用终端输出的Forwarding地址访问 - 检查本地防火墙是否允许3000端口的入站连接,临时关闭防火墙测试是否解决问题
5. 前端Socket.io配置配合
若开启了credentials: true,前端连接时需添加对应配置:
const socket = io('你的服务地址', { withCredentials: true });
内容的提问来源于stack exchange,提问作者ali-kingX
相关产品推荐
相关产品推荐

