1、什么是WebSocket?
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
如果通过ajax实现双向通信,需要写一个定时器不断请求,即轮询,然后以固定的间隔给服务器发请求,询问服务器有没有新消息。
1)浪费服务器资源,不断地连接和断开请求资源
2)浪费带宽资源
3)实时性不够
2、WebSocket与HTTP区别
在http中
response是被动的不能主动发起,一个request只能有一个response,应答机制
http1.0中,一个request一个response,这次http请求就结束了
http1.1中,有一个keep-alive,在一个http连接中,可以发送多个request、接收多个response
在WebSocket中
浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
3、WebSocket的特点
1)建立在TCP协议之上,服务器端的实现比较容易
2)与HTTP协议有着良好的兼容性,默认端口也是80和443,握手阶段采用HTTP协议,握手时不容易屏蔽,能通过各种HTTP代理服务器
3)数据格式比较轻量,性能开销小,通信高效
4)可以发送文本,也可以发送二进制数据,无需对数据进行转换所以性能高
5)没有同源限制,客户端可以与任意服务器通信,实现跨域
6)协议标识符是ws(如果加密,则是wss),服务器网址是URL
7)双向通信
4、WebSocket连接如何创建
连接是由浏览器发起的,是标准的HTTP报文格式,但会跟HTTP不同
客户端 发给 服务端
Upgrade: websocket 协议升级,升级成websocket
Connection: Upgrade 协议升级,升级成websocket
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== 为了校验对方知道自己在说什么,websocket才懂的话,浏览器随机生成的,发给服务器问对方来验证
Sec-WebSocket-Protocol: chat, superchat 用户定义的字符串,用来区分同URL下,不同的服务所需要的协议
Sec-WebSocket-Version: 13 目前websocket版本
服务端 返回给 客户端
HTTP/1.1 101 Switching Protocols
Upgrade: websocket 告诉客户升级的是WebSocket协议,而不是其他协议
Connection: Upgrade
Sec-WebSocket-Accept: 随机的string 经过服务器确认,并且加密过后的Sec-WebSocket-Key服务器回应客户端,知道了,给他看ID CARD来证明
101是协议已经切换的状态码
至此,http已经完成任务,接下来安全按照WebSocket协议进行
5、入门例子
1)使用Socket.io
可以使用socket.io这个库,这个库使用简单方便、可以兼容IE5、自动数据解析。
nom init
npm i socket.io -D
客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="http://localhost:8080/socket.io/socket.io.js"></script>
<!-- socket.io自动返回这个文件 -->
<script>
//1、 让服务器知道这个不是一个普通的http请求
// 2、直接、天生自带跨域
// 这个sock对象跟服务端的sock对象一样.
let sock=io.connect('ws://localhost:8080/');
//sock.emit('name',数据) 发送
//sock.on('name',function(数据){}) 接收 这里的数据是客户端传过来的
sock.emit('add',3,4);
sock.on('timer',timer=>{
console.log(timer);
})
</script>
</head>
<body>
</body>
</html>
服务端:
websocket不是一个独立的协议,在建立连接时,依赖http
const http=require('http');
const io=require('socket.io');
//1、建立http
let server=http.createServer((req,res)=>{});
server.listen(8080);
//2、建立ws
//listen一个http服务 监听发现找ws就会把服务抢过来
let wsServer=io.listen(server);
wsServer.on('connection',sock=>{
//sock.emit('name',数据) 发送
//sock.on('name',function(数据){}) 这里的数据是客户端传过来的
sock.on('add',function(a,b){
console.log(a+b);
});
setInterval(function(){
sock.emit('timer',new Date().getTime());
},1000);
})
服务端输出7,前端console每隔1s输出时间
接下来不用库,用原生方法实现一下连接
2)原生实现
原生要自己写返回
这里说一下Sec-WebSocket-Accept 计算:
key=>http的头里来
uuid=> 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 这一串是作者规定的
通过SHA1计算出摘要,并转成base64字符串,得出结果
result=>base64(sha1(key+uuid))
客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script>
let ws= new WebSocket('ws://localhost:8080');
ws.onopen=function(){
alert('建立连接')
} //连接打开后 双方握手结束后才open
ws.onmessage=function(){} //有消息过来
ws.onclose=function(){} //双方协商关闭,正常关闭时
ws.onerror=function(){} //有错误时
</script>
</head>
<body>
</body>
</html>
服务器:
const net=require('net');
const crypto=require('crypto');
function parseHeader(str){
let arr=str.split('\r\n').filter(line=>line);//去掉空行
arr.shift();//第一行去掉
let headers={};
arr.forEach(line=>{
let [name,value]=line.split(':');//每一行通过: 来切开,变成数组
name=name.replace(/^\s+|\s+$/g, '').toLowerCase();//名字 将头尾的空格去掉 变成小写
value=value.replace(/^\s+|\s+$/g, '');
headers[name]=value;
});
return headers;
}
let server=net.createServer(sock=>{
sock.once('data',buffer=>{
let str=buffer.toString(); //直接toString可以 都是文字
let headers=parseHeader(str);
if(headers['upgrade']!='websocket'){
console.log('no ungrade');
sock.end();
}else if(headers['sec-websocket-version']!='13'){
console.log('no 13');
sock.end();
}else{
let key=headers['sec-websocket-key'];
let uuid='258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
let hash=crypto.createHash('sha1');//创建一个hash对象,签名算法是sha1
hash.update(key+uuid);
let result=hash.digest('base64');//输出base64
//101是协议已经切换的状态码
sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection:upgrade\r\nSec-Websocket-Accept:${result}\r\n\r\n`);
}
});
sock.on('end',function(){
console.log('已断开');
});
});
server.listen(8080);
在调试器可以看到pending
本文是本人在学习过程中整理的笔记,如有不正确请指出~
参考:
https://www.cnblogs.com/nnngu/p/9347635.html