Socket.io双向通信教程,cors跨域通信解决方案
发布于 作者:苏南大叔 来源:程序如此灵动~

本文主要描述socket.io
面临的跨域问题,也就是说html
伺服的页面和socket.io
服务器不在一起的情况。因为socket.io
底层可能是http
的polling
,还可能是基于websocket
协议的。如果基于polling
的时候,就不可避免的会遇到跨域的问题,这就是非常容易理解的老生常谈的问题。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。测试环境:win10
,nodejs@20.18.0
,express@4.21.2
,socket.io@4.8.1
。
前文回顾
在socket.io
的第一篇文章里面,苏南大叔就提到过:socket.io
服务是可以单独运行的,不一定非要配合express
或者http
模块。配合express
或者http
复用端口的原因,仅仅在于防止发生跨域的问题。
- https://newsn.net/say/socket-io.html
- https://newsn.net/say/socket-io-path.html
- https://newsn.net/say/socket-io-transport.html
- https://newsn.net/say/socket-io-broadcast.html
分开部署服务端和客户端
而本文主要讲述跨域问题,所以就分开运行socket.io
和html
文件。
服务端(node t.js):
const socketio = require("socket.io");
const io = socketio(3000);
io.on("connection", (socket) => {
socket.emit("message", "服务器欢迎你");
socket.on("message", (msg) => {
socket.emit("message", "服务器收到你的消息了," + msg);
});
});
客户端(nginx):
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io("http://localhost:3000");
socket.emit('message', "Hello, Server!");
socket.on('message', message => {
console.log(message);
});
</script>
跨域
直接这样写的话,正常情况下,会显示跨域相关错误提示。报错信息如下:
test.html:1 Access to XMLHttpRequest at 'http://localhost:3000/socket.io/?EIO=4&transport=polling&t=2aveh30e' from origin 'http://localhost' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
如下图所示:
服务地址和协议
socket.io
监听3000
端口,而test.html
通过nginx
放在80
端口。
服务端地址会有两个表述,在客户端代码中,作为一个引导作用来说,这两种表述都是可以的。在这个上下文环境里面,两者是没有区别的。
- http://localhost:3000
- ws://localhost:3000
结论就是:socket.io
自己单独使用的时候,也一样可以提供http
和ws
两种服务。
跨域解决方案一(切换到ws)
跨域问题,都是http
协议引起的。所以,可以通过强制使用ws
协议,来规避这个可能存在的跨域问题。但是,小概率情况下,会有兼容性问题。
服务端:
const socketio = require("socket.io");
const io = socketio(3000, { transports: ['websocket'] });
// ...
客户端:
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io("ws://localhost:3000", { transports: ['websocket'] });
// ...
</script>
跨域解决方案二(服务端设置允许*)
服务端:
const socketio = require("socket.io");
const io = socketio(3000, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
// ...
不过,任何严谨的人,都不会接受origin:*
的方案。可以改成:
const socketio = require("socket.io");
const io = socketio(3000, {
cors: {
origin: "http://localhost",
methods: ["GET", "POST"]
}
});
// ...
跨域解决方案三(官方推荐)
在socket.io
的官方,存在一个解决方案描述:
根据官方文档,修改方案如下:
服务端:
const socketio = require("socket.io");
const io = socketio(3000, {
cors: {
origin: 'http://localhost', // 允许访问的域名
methods: ['GET', 'POST'], // 允许的请求方法
allowedHeaders: ['my-custom-header'], // 允许的自定义请求头
credentials: true // 允许发送身份凭证(如cookies)
}
});
// ...
客户端:
<script src="/socket/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000', {
withCredentials: true,
extraHeaders: {
"my-custom-header": "uncle sunan's socket.io demo"
}
});
// ...
</script>
结语
更多苏南大叔的socket.io
文章,请点击苏南大叔的博客文章:


