是什么
由于TCP是服务器被动通信,无法主动向客户端发送信息,所以有了websocket 可以双向通信 服务端监听自身端口+客户端连接服务器地址和该端口,达成通信成功!(类似于Java的socket通信)
使用的是Http连接来建立:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信。 (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL(ws://example.com:80/some/path)。
参考:https://www.ruanyifeng.com/blog/2017/05/websocket.html
如何建立
必须由浏览器发起!因为是一个HTTP请求 请求头:
GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13
注意点:
- 地址以
ws://目标地址:端口 开始 - connection指定连接升级
- upgrade指定升级成websocket协议
- 13是websocket协议版本号
返回值:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string
注意:101标识HTTP协议将被替代成websocket协议!标识连接成功!
安全的WebSocket连接机制和HTTPS类似。首先,浏览器用wss://xxx创建WebSocket连接时,会先通过HTTPS创建安全的连接,然后,该HTTPS连接升级为WebSocket连接,底层通信走的仍然是安全的SSL/TLS协议
websocket 常用 api
WebSocket WebSocket(url)
webSocket.onclose
webSocket.onerror
webSocket.onmessage
webSocket.onopen
webSocket.readyState
"close"
"error"
"message"
"open"
WebSocket.close([code[, reason]])
WebSocket.send(data)
向服务器发送数据
发送文本:ws.send('your message'); 发送二进制:
var file = document
.querySelector('input[type="file"]')
.files[0];
ws.send(file);
发送arraybuffer:
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
ws.send(binary.buffer);
判断发送进度:实例对象的bufferedAmount属性,表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。
var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
} else {
}
处理服务器发过来的数据
发送过来的数据包裹在event.data 中
ws.onmessage = function(event) {
var data = event.data;
};
ws.addEventListener("message", function(event) {
var data = event.data;
});
对于接受的数据,需要判断下是什么类型,然后做处理
ws.onmessage = function(event){
if(typeof event.data === String) {
console.log("Received data string");
}
if(event.data instanceof ArrayBuffer){
var buffer = event.data;
console.log("Received arraybuffer");
}
}
可以显式指定二进制的数据类型
ws.binaryType = "blob";
ws.onmessage = function(e) {
console.log(e.data.size);
};
一个完整示例
客户端:
;(function(){
const domList = document.querySelector("#list");
const domMsg = document.querySelector("#msg");
const domSend = document.querySelector("#send");
const username = localStorage.getItem('username');
if(username == null || username == ""){
location.href = '/entry.html';
return;
}
const ws = new WebSocket("ws:localhost:8000");
function init(){
bindEvent();
}
function bindEvent(){
domSend.addEventListener('click',handleSendMsg,false);
ws.addEventListener('open',onOpen,false);
ws.addEventListener('close',onClose,false);
ws.addEventListener('error',onError,false);
ws.addEventListener('message',onMessage,false);
}
function handleSendMsg(){
ws.send(JSON.stringify({
"username":username,
"msg":domMsg.value,
"time":new Date()
}))
domMsg.value = "";
}
function onOpen(){
console.log('open')
}
function onClose(){
console.log('close')
}
function onError(){
console.log('error')
}
function onMessage(e){
console.log('message',e)
domList.appendChild(createMsg(JSON.parse(e.data)))
}
function createMsg(data){
const {username,msg,time} = data;
const li = document.createElement('li');
li.innerHTML = `
<p><span>${username}</span> <i>${time}</i></p>
<p>${msg}</p>
`
return li;
}
init();
})();
服务器:这里使用ws包来监听
const WS = require("ws");
(function () {
const server = new WS.Server({ port: 8000 });
function init() {
bindEvent();
}
function bindEvent() {
server.on("open", onOpen);
server.on("close", onClose);
server.on("error", onError);
server.on("connection", onConnection);
}
function onOpen() {
console.log("open");
}
function onClose() {
console.log("close");
}
function onError() {
console.log("error");
}
function onMessage(msg) {
console.log("message", JSON.parse(msg));
server.clients.forEach((c) => c.send(msg, { binary: false }));
}
function onConnection(clinet) {
console.log("connection");
clinet.on("message", onMessage);
}
init();
})();
后端可以使用nodemon来监控这个index.js文件,并启动服务! 前端可以使用vite来启动服务!
"scripts": {
"dev": "nodemon index.js"
},
"scripts": {
"dev":"vite"
},
|