WebSocket

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值