websocket 详解,与node.js完成即时通讯案例

1. 前言

之前,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,WebSocket 能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。它是一种在单个 TCP 连接上进行全双工通讯的协议。

单工、半双工和全双工是电信计算机网络中的三种通信信道。
websocket

  • 单工,表示只支持数据在一个方向上传输,在同一时间只有一方能接受或发送信息,例如:电视、广播、收音机。
  • 半双工,数据传输允许数据在两个方向上传输,但是在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信,例如:对讲机。
  • 全双工,数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,在同一时间可以同时接受和发送信息,例如:电话。

2. 简介

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,然后,浏览器和服务器之间就形成了一条快速通道,两者之间就直接可以创建持久性的连接,并进行双向数据传输

WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。

特点:

  1. 服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。
  2. 建立在 TCP 协议之上,服务器端的实现比较容易。
  3. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  4. 数据格式比较轻量,性能开销小,通信高效。
  5. 可以发送文本,也可以发送二进制数据。
  6. 没有同源限制,客户端可以与任意服务器通信。
  7. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

3. 客户端使用

3.1 webSocket构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。

var ws = new WebSocket('ws://localhost:8088');

参数为指定连接的 URL。执行上面语句之后,客户端就会与服务器进行连接。

3.2 webSocket 属性

属性描述
Socket.readyState只读属性 readyState 表示连接状态
Socket.bufferedAmount只读属性 bufferedAmount 表示已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

3.2.1 Socket.readyState

readyState属性返回实例对象的当前状态,可以是以下值:

  • 0 - 状态 - CONNECTING,表示连接尚未建立。
  • 1 - 状态 - OPEN,表示连接已建立,可以进行通信。
  • 2 - 状态 - CLOSING,表示连接正在进行关闭。
  • 3 - 状态 - CLOSED,表示连接已经关闭或者连接不能打开。
if (ws.readyState === 2 || ws.readyState === 3) {
	console.log("连接已断开");
}

3.2.2 Socket.bufferedAmount

只读属性 bufferedAmount 表示还有多少字节的二进制数据没有发送出去。它可以用来判断发送是否结束。

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 发送完毕
} else {
  // 发送还没结束
}

3.3 webSocket 事件属性

实例对象的事件属性,属性值为一个回调函数,如果要指定多个回调函数,可以使用addEventListener方法。

事件事件处理程序描述
openSocket.onopen连接建立时触发
messageSocket.onmessage客户端接收服务端数据时触发
errorSocket.onerror通信发生错误时触发
closeSocket.onclose连接关闭时触发
//连接建立时触发
ws.onopen = () => {
    console.log("open...")
}
ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});
//客户端接收服务端数据时触发
ws.onmessage = (msg) => {
    console.log(msg.data);
}
ws.addEventListener("message", function(msg) {
  console.log(msg.data);
});
//通信发生错误时触发
ws.onerror = () => {
    console.log('error')
}
//连接关闭时触发
ws.onclose = () => {
    console.log('close')
}

3.4 webSocket 方法

方法描述
Socket.send()向服务器发送数据

send()方法用于向服务器发送数据。数据格式可以是String、Blob、ArrayBuffer。

ws.send('your message');

4. 服务端使用

这里我们使用node.js 来实现 websocket的服务端。

4.1 安装ws模块

npm install ws
//或者使用yarn安装
yarn add ws

4.2 创建websocket实例

const WebSocket = require("ws");
 
const wServer = new WebSocket.Server({
	host:[string], //主机名
	port:[number], //端口号
	backlog:[number], //未完成的连接队列的最大长度
	server:[http.server], //一个预先创建的Node.js HTTP/S服务器
	handleProtocols:[Function], //用来处理WebSocket子协议的函数
	path:[string], //路径
	noServer:[boolean], //不启用任何服务器模式
	clientTracking:[boolean], //指定是否跟踪客户端
	maxPayload:[number] //允许的最大消息大小(以字节为单位)
});

4.3 方法

  • connection,通讯连接完成后触发。
  • message,收到客户端的消息后触发。
  • close,客户端中断连接时触发。
  • send(),给客户端发送消息。
wServer.on("connection",(client)=>{
	console.log("On the connected");
	//给客户端发送消息
    client.send("welcom to back");
    
    client.on("message",(msg)=>{
        //收到客户端的消息
    })

    client.on("close",()=>{
		//与客户端中断连接
    })
})

5. 案例

完成即时通讯。

客户端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="msglist"></div>
    <textarea id="msg" rows="10" cols="40"></textarea>
    <button id="btn">留言</button>
    <script>
        var $btn = document.querySelector("#btn");
        var $msg = document.querySelector("#msg");
        var $list = document.querySelector('#msglist');
		//实例化websocket
        var ws = new WebSocket("ws://localhost:8088");
	
        wsEvent();
		//执行事件
        function wsEvent() {
            //收到服务端发来的数据 
            ws.onmessage = (msg) => {
                $list.innerHTML += msg.data + "</br>";
            }
            ws.onclose = () => {
                //如果连接中断,重新连接
                reconnect()
            }
            ws.onerror = () => {
                //如果连接中断,重新连接
                reconnect()
            }
        }
        $btn.addEventListener("click", () => {
        	//发送数据给服务端
            ws.send($msg.value);
        })

        function reconnect() {
        //重新连接
            if (ws.readyState === 2 || ws.readyState === 3) {
                ws = new WebSocket('ws://localhost:8099');
                wsEvent();
            }
        }
    </script>
</body>
</html>

服务端代码:

const ws = require("ws");
//创建socket服务
var websocketServer=new ws.Server("http://localhost:8088");
//用来存放客户端的数组
var clientList=[];
//客户端的标识
var id=0;

websocketServer.on("connection",(client)=>{
	//通讯连接后,给客户端添加标识,将客户端添加到数组中
    client.id="name"+ id++;
    clientList[id]=client;
	//给客户端发送数据
    client.send("welcom to back");
    //收到客户端发来的数据
    client.on("message",(msg)=>{
    	//将数据发给所有连接上的客户端
        boardcast(client.id+":"+msg);
    })
    client.on("close",()=>{
    	//如果客户端断开连接
        boardcast(client.id+"下线了");
        delete clientList[client.id];
    })
})

function boardcast(msg){
	//将数据发给所有连接上的客户端
    for(let prop in clientList){
        clientList[prop].send(msg);
    }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值