WebSocket使用及优化(心跳机制与断线重连)https://juejin.cn/post/6945057379834675230
vue项目使用WebSocket技术 https://juejin.cn/post/6982078455722557448
1.资源请求标签【link、img】-缺点:只能发起get请求,无法接收响应数据
let img = new Image();
img.src = '[http://192.168.4.154](http://192.168.4.154/):4005/list';
let link = document.createElement('link');
link.setAttribute("rel", "stylesheet");
link.setAttribute('href', '[http://192.168.4.154](http://192.168.4.154/):4005');
document.head.append(link); // 只设置href不会请求,必须添加才会请求
document.head.removeChild(link);
2.JSONP【script标签】
要求服务端将返回数据和传过去的全局函数名称组织成可执行的js代码,缺点:只能发起get请求,随意执行响应数据,不安全
window.execJson = function (dataJson) {
console.log(dataJson);
};
let scripte = document.createElement('script');
scripte.setAttribute('src', '[http://192.168.4.154](http://192.168.4.154/):4005/jsonp?funName=execJson');
document.body.append(scripte);
document.body.removeChild(scripte);
组织成可执行的js代码.png
3.iframe+form发送post请求-无法接收响应数据
const requestPost = ({ url, data }) => {
const iframe = document.createElement('iframe');
iframe.name = 'iframePost';
iframe.style.display = 'none';
document.body.appendChild(iframe);
const form = document.createElement('form');
const node = document.createElement('input');
iframe.addEventListener('load', function () {
console.log('post success');
document.body.removeChild(this);
});
form.action = url;
// 在指定的iframe中执行form
form.target = iframe.name;
form.method = 'post';
for (let name in data) {
node.name = name;
node.value = data[name].toString();
form.appendChild(node.cloneNode());
}
// 表单元素需要添加到主文档中.
form.style.display = 'none';
document.body.appendChild(form);
form.submit();
// 表单提交后,就可以删除这个表单,不影响下次的数据发送.
document.body.removeChild(form);
}
requestPost({
url: '[http://192.168.4.154](http://192.168.4.154/):4005/postData',
data: { name: 'wxm' }
});
4.CORS 跨域资源共享-需要服务端设置允许某些访问源
image.pngserver.get('/open', function (request, response) {
console.log('get请求了open');
// 允许哪些源访问,可以是通配符*
response.setHeader("Access-Control-Allow-Origin", "[http://192.168.4.154](http://192.168.4.154/):4004");
// 是否允许携带验证信息(非必须,如果请求携带了证书,需要设为true) Credentials-证书
response.setHeader("Access-Control-Allow-Credentials", true);
response.setHeader("wxm-en", "ha");
response.setHeader("hu-en", "haha");
// 设置允许客户端访问的响应头
response.setHeader("Access-Control-Expose-Headers", "wxm-en");
response.send('open请求成功');
});
fetch('[http://192.168.4.154](http://192.168.4.154/):4005/open', {
// 默认情况下为omit:不发送;same-origin:同源情况下发送;include:都发送
// 如果设置了发送,但服务端未设置Access-Control-Allow-Credentials或设为了false都无法请求
credentials: 'include',
}).then(res => {
res.text().then((r) => {
console.log(r);
});
});
Access-Control-Expose-Headers 仅设置了wxm-en没设置hu-en则访问不到hu-en :
image.png
fetch('[http://192.168.4.154](http://192.168.4.154/):4005/post/open', {
method: 'post',
headers: {
"Content-type": "application/x-www-form-urlencoded",
// 新增了一个头信息,此时该请求为非简单请求,会先发送一个预检请求,服务端也应处理这类预检请求
"member-name": "wxm",
},
body: JSON.stringify({ length: 100 })
}).then(res => {
res.text().then((r) => {
console.log(r);
});
})
preflight.png
OPTIONS.png
server.options('/post/open', function (request, response) {
console.log('接收到post open的预检请求');
response.setHeader("Access-Control-Allow-Origin", "[http://192.168.4.154](http://192.168.4.154/):4004");
// 用于preflight request中,列出了允许的首部信息
response.setHeader("Access-Control-Allow-Headers", "member-name");
response.setHeader("Access-Control-Allow-Methods", "*");
response.send('');
});
简单请求:①请求方式为GET/POST/HEAD ② 请求头不超过Accept、Accept-Language、Content-Language、Content-Type(只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
预检请求:假设没有预检请求,跨域请求直接到达服务器,并有可能对数据库里的数据进行了操作,但是返回的结果被浏览器拦截了,那么我们就获取不到返回结果,这是一次失败的请求,但是可能对数据库里的数据产生了影响
// 浏览器必须先使用OPTIONS方法发起一个预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求
5.postMessage -- 建立在页面基础上的跨域通信
image.png let iframe = document.getElementById('crossOrigin'); // 页面内嵌了一个非同源的页面
iframe.contentWindow.postMessage("message info", "[http://192.168.4.154](http://192.168.4.154/):4006");
// 虽然能互相拿到引用但无法互相访问
image.png
// 但可以通过postMessage 进行通信:
onMessage.png
接收到通过postMessage发送的信息.png
6.WebSocket
不限制非同源连接,实现客户端与服务端的通信
服务端也可以将接收到的信息发给其他已连接的客户端,实现客户端与其他客户端的通信
let wsUrl = 'ws://192.168.4.154:4007';
let ws = new WebSocket(wsUrl);
ws.onopen = function () {
console.log('ws连接成功!');
}
ws.onmessage = function (event) {
console.log(event.data);
}
window.onunload = function () {
ws.close();
}
let msgInput = document.getElementById('messgae');
document.getElementById('send').onclick = function () {
ws.send(msgInput.value); // 向服务端发送信息
};
// WebSocket实例的属性
{
binaryType: "blob", // blob:Binary Large Object
bufferedAmount: 0, // 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数
extensions: "",
onclose: null,
onerror: null,
onmessage: null,
onopen: null,
protocol: "",
// 0未连接(WebSocket.CONNECTING)
// 1已连接(WebSocket.OPEN)
// 2正在关闭(WebSocket.CLOSING)
// 3已关闭(WebSocket.CLOSED)
readyState: 3,
url: "ws://192.168.4.154:4007/"
}
// 心跳检测
var heartBeat = {
interval: 1 * 1000,
timeout: 3 * 1000, // 发出后timeout时间内还未收到信息则认为连接已断开
wsUrl: 'ws://192.168.4.154:4007',
ws: null,
isConnecting: false,
assertConnect: false,
startHeartBeat() {
setTimeout(() => {
if (this.ws.readyState === WebSocket.OPEN) {
console.log('<乒乒');
this.ws.send('wxm');
this.waitServer();
}
}, this.interval);
},
waitServer() {
this.assertConnect = false;
setTimeout(() => {
if (this.assertConnect) { // 如果保持连接
this.startHeartBeat();
} else {
console.log('无响应,需重新连接...');
this.reConnect();
}
}, this.timeout);
},
reConnect() {
if (this.isConnecting) {
return;
}
try {
this.ws.close();
console.log('重新连接前先关闭连接...');
} catch (err) {
console.log('连接已关闭,无需再次关闭');
}
this.init();
},
init() {
console.log('连接中...');
this.isConnecting = true;
let ws = new WebSocket(this.wsUrl);
this.ws = ws;
ws.onopen = () => {
console.log('已连接√');
this.isConnecting = false;
this.startHeartBeat();
};
ws.onerror = () => {
console.log('连接失败');
this.isConnecting = false;
this.reConnect();
};
ws.onclose = () => {
this.isConnecting = false;
console.log('客户端:连接已关闭');
};
ws.onmessage = (e) => {
// 接收的如果是心跳消息
let isHeartMsg = true;
if (isHeartMsg) {
console.log('乓乓>');
this.assertConnect = true;
}
};
},
}
网友评论