最近看人家用socket.io聊天,於是自己也想打個服務試試,不試不知道,一試嚇一跳,原來這里嗎還有這么多坑,下面就是所遇的坑,記錄一哈,但願可以幫助到遇到同樣的坑的小伙伴
文章目錄
一、服務端配置
這里我使用nodejs 作為我的后台服務
1、首先創建一個目錄
D:\learnVUE>mkdir socketIOTest
2、再創建一個nodejs項目目錄
D:\learnVUE>cd socketIOTest
D:\learnVUE\socketIOTest>mkdir nodejsProj
3、初始化一個nodejs項目
D:\learnVUE\socketIOTest>cd nodejsProj
D:\learnVUE\socketIOTest\nodejsProj>npm init -y
4、安裝依賴
D:\learnVUE\socketIOTest\nodejsProj>npm i express -S
D:\learnVUE\socketIOTest\nodejsProj>npm i cors -S
D:\learnVUE\socketIOTest\nodejsProj>npm i socket.io -S
5、再在目錄下新增一個app.js
在app.js中增加如下類容
/* * @Descripttion: * @version: * @Author: dex * @Date: 2021-01-21 18:01 * @LastEditors: dex * @LastEditTime: 2021-01-21 18:01 * */
const app = require("express")();
var http = require("http").createServer(app);
var socket = require("socket.io")(http, {
transports: ["websocket"],
});
app.get("/", function (req, res) {
res.send("<h1>你好web秀</h1>");
});
//設置跨域訪問
app.all("*", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", " 3.2.1");
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
// socket.on("connect", function (socket) {
// console.log("conneted ...", socket);
// });
// 監聽客戶端連接
socket.on("connection", function (socket) {
console.log("客戶端有連接");
socket.on("connect", () => {
console.log("客戶端開始連接");
});
// 監聽客戶端斷開
socket.on("disconnect", () => {
console.log("客戶端斷開");
});
// 給客戶端發送消息
socket.emit("welcome", "歡迎連接socket");
// 監聽客戶端消息
socket.on("hello", (data) => {
console.log("接收客戶端發送數據", data);
});
});
// 啟動服務器 監聽 8088 端口
http.listen(3000, function () {
console.log("server runing at 127.0.0.1:3000");
});
6、運行測試
D:\learnVUE\socketIOTest\nodejsProj>node app.js
server runing at 127.0.0.1:3000
二、創建一個vue項目
1、創建一個vue目錄
D:\learnVUE\socketIOTest> vue init webpack vueTestSocketIo
2、安裝socket依賴
D:\learnVUE\socketIOTest\vueTestSocketIo>npm i vue-socket.io -S
3、修改main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import VueSocketIO from "vue-socket.io";
Vue.config.productionTip = false;
// 注冊
Vue.use(
new VueSocketIO({
debug: true, // debug調試,生產建議關閉
connection: "http://localhost:3000"
})
);
new Vue({
// 這里為全局監聽socket事件消息,監聽函數這里只寫了一點,其實很有很多事件。
sockets: {
connecting() {
console.log("正在連接");
},
disconnect() {
console.log("Socket 斷開");
},
connect_failed() {
cosnole.log("連接失敗");
},
connect() {
console.log("socket connected");
}
},
router,
render: h => h(App)
}).$mount("#app");
4、修改HelloWorld.vue
<template>
<div id="app55">
<div id="nav55">
<button @click="connect">建立連接</button>
<button @click="sendMessage">發送數據</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {};
},
methods: {
// 連接socket
connect() {
this.$socket.open(); // 開始連接socket
// 訂閱事件
this.sockets.subscribe("welcome", data => {
console.log("welcome data==> ", data);
});
},
// 發送消息
sendMessage() {
this.$socket.emit("hello", "這里是客戶端");
}
},
sockets: {
connect(data) {
console.log(data);
},
//
welcome: data => {
console.log("welcome data數據返回= >", data);
}
},
beforeDestroy() {
this.sockets.unsubscribe('welcome')
}
};
</script>
5、啟動項目
D:\learnVUE\socketIOTest\vueTestSocketIo>npm run dev
I Your application is running here: http://localhost:8080
三、項目測試
滿懷期待的啟動項目,打開瀏覽器進行測試卻得到了如下結果
想想是不是因為http的原因,那就試着將其部署到自己的https 服務器里面去,於是就開始着手下面的docker發布了
四、使用docker發布服務
1、配置Dockerfile
首選當然是把nodejs 項目搞到服務器,再配置一個Dockerfile這些都不多說了,可以看我另一篇docker發布vue
[root@dex nodejsProj]# ll
total 28
-rw-r--r-- 1 root root 707 Jan 22 06:20 app2.js
-rw-r--r-- 1 root root 1647 Jan 30 16:04 app.js
-rw-r--r-- 1 root root 581 Jan 22 10:00 Dockerfile
-rw-r--r-- 1 root root 1139 Jan 22 06:20 index.js
-rw-r--r-- 1 root root 452 Jan 22 09:57 package.json
drwxr-xr-x 2 root root 4096 Jan 22 06:20 public
drwxr-xr-x 2 root root 4096 Jan 22 06:20 views
其中Dockerfile類容也挺簡單,就幾行命令,看(這里要注意的是node鏡像的版本不能太低))
FROM docker.io/node:12.19.0
# Create app directory
WORKDIR /usr/src/socketIoTestConnection
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
# configure npm source from taobao
#RUN npm install -g cnpm --registry=https://registry.npm.taobao.org
RUN npm config set package-lock false
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
EXPOSE 9019
CMD [ "npm", "start" ]
2、創建鏡像
[root@dex nodejsProj]# docker build -t socketio_node_serve:v1 .
[root@dex ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
socketio_node_serve v1 53f997eed584 23 hours ago 930MB
3、創建容器
[root@dex nodejsProj]# docker run -p 8431:3000 --name socketio_serve -d socketio_node_serve:v
[root@dex nginx]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f474d019a61d socketio_node_serve:v1 "docker-entrypoint.s…" 23 hours ago Up 23 hours 9019/tcp, 0.0.0.0:8431->3000/tcp socketio_serve
好了容器服務跑起來了,接下來是配置一個nginx 把我的容器端口映射到外網上去,讓客服端來訪問
五、配置nginx代理
在nginx 中最開始的配置時是這樣的
server {
listen 443 ssl;
location /socketioserve/ {
proxy_pass http://localhost:8431/;
}
}
然后在頁面修改連接地址來進行訪問
// 注冊
Vue.use(
new VueSocketIO({
debug: true, // debug調試,生產建議關閉
connection: "https://benpaodehenji.com/socketioserve"
})
);
發現根本就無法訪問,於是找了一些資料,將其修改為如下配置
location /socket.io/{
proxy_pass http://127.0.0.1:8431;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 60;
proxy_read_timeout 600;
proxy_send_timeout 600;
}
六、跨域處理
發現是可以訪問了,只是跨域的問題依舊存在
Access to XMLHttpRequest at 'https://benpaodehenji.com/socket.io/?EIO=4&transport=polling&t=NSiqHnZ' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
failed: Error during WebSocket handshake: Unexpected response code: 502
使用官方方法檢測
使用cmd 進行如下測試
C:\Users\Lenovo>curl https://benpaodehenji.com/socket.io/?transport=polling
{"code":0,"message":"Transport unknown"}
看這個結果發現是配置的問題,於是把app.js中
var socket = require("socket.io")(http, {
transports: ["websocket"],
});
修改為
var socket = require("socket.io")(http);
再度測試
C:\Users\Lenovo>curl https://benpaodehenji.com/socket.io/?transport=polling
0{"sid":"IXY8bzxMcCmwXunTAAAN","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}
C:\Users\Lenovo>
發現配置已經和官方說的一樣了,但是如下的跨域任然無法處理
在萬般無奈下我單獨創建了一個testConnectSocketio.html使用它來直接連接測試
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>testconnect</title>
</head>
<body>
<h6>test</h6>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.0/socket.io.js"></script>
<script language="JavaScript">
let socket = io('https://benpaodehenji.com',{
transports: ["websocket"]
});
socket.emit('hello', {msg:'hi, server'});
// socket.on()鐢ㄤ簬鎺ユ敹鏈嶅姟絝彂鏉ョ殑娑堟伅
socket.on('welcome', data=>{
console.log('client connect server',data);
});
socket.on('disconnect', ()=>{
console.log('client disconnect');
});
</script>
</body>
</html>
測試 客戶端發起連接:
測試服務端響應:
2021-01-23T01:14:07.165041305Z > nodejsProj@1.0.0 start /usr/src/socketIoTestConnection
2021-01-23T01:14:07.165046213Z > node app.js
2021-01-23T01:14:07.165049447Z
2021-01-23T01:14:07.336723288Z server runing at 127.0.0.1:3000
2021-01-23T01:14:09.047098330Z 客戶端有連接
2021-01-23T01:17:17.786180956Z 客戶端斷開
2021-01-23T01:24:49.561294438Z 客戶端有連接
2021-01-23T01:24:49.624188505Z 接收客戶端發送數據 { msg: 'hi, server' }
看上面的測試充分證明了我的服務端配置時ok的,但問題又出在哪里呢?
再回去看一次官網的處理方案,
於是再次將app.js中的配置
var socket = require("socket.io")(http);
修改為:
var socket = require("socket.io")(http, {
cors: {
origin: "https://benpaodehenji.com",
methods: ["GET", "POST"]
}
});
改完后刪除容器,刪除鏡像,再重復上面第四步中的2、3步流程從頭再來,希望這次可以成功…
測試
哎。這這這??
有回到官網,又發現了這一段:
於是趕緊配置
七、處理400異常
這次測試跨域提示已經沒有了,但是測試又提示400錯誤
https://benpaodehenji.com/socket.io/?EIO=3&transport=polling&t=NTI4Tsg 400 (Bad Request)
於是到處收集資料,但是收獲甚微…
A few days later…
再次回來測試項目,這次點擊Network請求, 發現了如下錯誤
於是順藤摸瓜,根據如上錯誤提示,最后在https://socket.io/docs/v3/migrating-from-2-x-to-3-0/index.html和
https://socket.io/blog/socket-io-3-1-0/發現了如下描述
這讓我眼前一亮,是否發現了一棵救命稻草
說干就干,於是又在app.js增加 allowEIO3: true, 將配置修改如下
var socket = require("socket.io")(http, {
allowEIO3: true,
cors: {
origin: "http://localhost:8080",
methods: ["GET", "POST"],
credentials: true
}
});
再次生成鏡像,運行容器測試…
哇 終於看見了希望
這里還有一個要注意cors
中的origin
不能配置為*
,否者還是會提示如下跨域
Access to XMLHttpRequest at 'https://benpaodehenji.com/socket.io/?EIO=3&transport=polling&t=NTIIi9A' from origin 'https://localhost:8082' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
要配置為真實的客服端的IP如
cors: {
origin: "https://localhost:8080",
methods: ["GET", "POST"],
credentials: true
}
ok 看測試也可以接受客服端的數據了,這次踩坑經歷基本結束。
最后最終修改好的app.js是這個樣子的,謝謝閱讀,希望可以為初次接觸socketio的朋友提供一點幫助謝謝
/*
* @Descripttion:
* @version:
* @Author: dex
* @Date: 2021-01-21 18:01
* @LastEditors: dex
* @LastEditTime: 2021-01-30 18:45
*/
const app = require("express")();
var http = require("http").createServer(app);
var socket = require("socket.io")(http, {
allowEIO3: true,
cors: {
origin: ['http://localhost:8080', 'http://localhost:8082'],
methods: ["GET", "POST"],
credentials: true
}
});
app.get("/", function (req, res) {
res.send("<h1>你好web秀</h1>");
});
//設置跨域訪問
app.all("*", function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", " 3.2.1");
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
// socket.on("connect", function (socket) {
// console.log("conneted ...", socket);
// });
// 監聽客戶端連接
socket.on("connection", function (socket) {
console.log("客戶端有連接");
socket.on("connect", () => {
console.log("客戶端開始連接");
});
// 監聽客戶端斷開
socket.on("disconnect", () => {
console.log("客戶端斷開");
});
// 給客戶端發送消息
socket.emit("welcome", "歡迎連接socket");
// 監聽客戶端消息
socket.on("hello", (data) => {
console.log("接收客戶端發送數據", data);
});
});
// 啟動服務器 監聽 8088 端口
http.listen(3000, function () {
console.log("server runing at 127.0.0.1:3000");
});