Browser和WebServer间的实时数据传输是一个很重要的需求,但最早只能通过AJAX轮询方式实现。在WebSocket标准没有推出之前,AJAX轮询是一种可行的方案。 AJAX轮询原理是设置定时器,定时通过AJAX同步服务端数据。这种方式存在延时且对服务端造成很大负载。直至2011年,IETF才标准化WebSocket - 一种基于TCP套接字进行收发数据的协议。 Socket.io将数据传输部分独立出来形成engine.io,engine.io对WebSocket和AJAX轮询进行了封装,形成了一套API,屏蔽了细节差异和兼容性问题,实现了跨浏览器/跨设备进行双向数据通信。 WebSocket 协议WebSocket是HTML5新增的一种通信协议,其特点是服务端可以主动向客户端推送信息,客户端也可以主动向服务端发送信息,是真正的双向平等对话,属于服务器推送技术的一种。 在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后浏览器和服务端之间就形成了一条快速通道,两者之间就直接可以数据相互传送,带来的好处是
为了建立一个WebSocket连接,浏览器首先要向服务器发起一个HTTP请求,这个请求和通常的HTTP请求不同,包含了一些附加头信息,其中附加头信息 为什么要使用WebSocket呢?Browser已经支持HTTP协议,为什么还要开发一种新的WebSocket协议呢? 我们知道HTTP协议是一种单向的网络协议,在建立连接后,仅允许Browser/UserAgent向WebServer发出请求资源后,WebServer才能返回对应的数据,而WebServer不能主动的推送数据给Browser/UserAgent。 最初这么设计HTTP协议的原因是,假设WebServer能主动的推送数据给Browser/UserAgent,那么Browser/UserAgent就太容易受到攻击了,一些广告商也会主动把广告在不经意间强行的传输给客户端,这不能不说是一个灾难。那么单向的HTTP协议给Web应用开发带哪些问题呢? 现在假设我们要开发一个基于Web的应用去获取当前WebServer的实时数据。例如股票实时行情、火车票剩余票数等。这就需要Browser/UserAgent与WebServer之间反复进行HTTP通信,Browser/UserAgent不断的发送请求去获取当前的实时数据。 常见的方式
Polling轮询是通过Browser/UserAgent定时向WebServer发送HTTP请求,WebServer收到请求后把最新的数据发回给Browser/UserAgent,Browser/UserAgent得到数据后将其显示,然后再定期重复此过程。 虽然这样可以满足需求,但仍存在问题,例如某段时间内WebServer没有更新的数据,但Browser/UserAgent仍然会定时发送请求过来询问,WebServer可以把以前的老数据再传送过去,Browser/UserAgent把这些没有变化的数据再显示出来。这样既浪费网络带宽,有浪费CPU利用率。 如果说把Browser/UserAgent发送请求的周期调大一些,就可以缓解这个问题,但如果WebServer的数据更新很快时,这样又不能保证Web应用获取数据的实时性。
LongPolling是对Polling的一种改进。 Browser/UserAgent发送HTTP请求到WebServer,此时WebServer可以做2件事情:
LongPolling的方式虽然在某种程度上减少了网络带宽和CPU利用率等问题,但仍存在缺陷。 例如WebServer的数据更新速度较快,WebServer在传送一个数据包给Browser/UserAgent后必须等待Browser的下一个HTTP请求到来,才能传递第二个更新的数据包给Browser。这样的话,Browser显示实时数据最快的时间为2 xRTT(往返时间)。另外在网络拥堵的情况下,这个应该是不能让用户接受的。另外,由于HTTP数据包的头部数据量很大(通常有400多个字节),但真正被服务器需要的数据却很少(有时只有10个字节左右),这样的数据包在网络上周期性传输,难免对网络带宽是一种浪费。 综上所述,要是在Browser有一种新的网路一些,能支持客户端和服务端的双向通信,而且协议的头部又不那么庞大就very nice了。WebSocket正是肩负这样的使命登上了Web的舞台。 WebSocket 原理WebSocket是一种双向通信协议,它建立在TCP之上,同HTTP一样通过TCP来传输数据,但与HTTP最大不同的是:
![]() 建立握手的时序图
简单说明下WebSocket握手的过程 当Web应用端调用
WebSocket与TCP、HTTP的关系WebSocket与HTTP协议一样都是基于TCP的,所以它们都是可靠的协议,Web开发者调用的 WebSocket和HTTP协议样都属于应用层协议,那么它们之间有没有什么关系呢? 答案是肯定的,WebSocket在建立握手连接时,数据是通过HTTP协议传输的。但在建立连接之后,真正的数据传输阶段是不需要HTTP参与的。 ![]() WebSocket与TCP和HTTP的关系
WebSocket Server如果要搭建一个WebServer,我们会有很多选择,市场上也有很多成熟的产品供我们是使用。例如开源的Apache,安装配置后即可工作。但如果想要搭建一个WebSocket服务器就没有那么轻松,因为WebSocket是一种新的通信协议,目前还是草案,没有成为标准,市场上也没有成熟的WebSocket服务器或Library实现WebSocket协议,我们必须自己手动编码去解析和组装WebSocket的数据包。要完成一个WebSocket服务器,估计所有的人都想放弃,不过市场上有几款比较好的开源Library可供使用。例如PyWebSocket、WebSocket-Node、LibWebSockets等,这些Library已经实现了WebSocket数据包的封装和解析,我们可以调用这些接口,这在很大程度上减少了我们的工作量。 Socket.io
由于HTTP是无状态的协议,要实现即时通讯非常困难。因为当对方发送一条消息时,服务器并不知道当前有哪些用户等着接收消息,当前实现即时通讯功能最为普遍的方式就是轮询机制。即客户端定期发起一个请求,看看有没有人发送消息到服务器,如果有服务端就将消息发给客户端。这种做法的缺点显而易见,那么多的请求将消耗大量资源,大量的请求其实是浪费的。 现在,我们有了WebSocket,它是HTML5的新API。WebSocket连接本质上就是建立一个TCP连接,WebSocket会通过HTTP请求建立,建立后的WebSocket会在客户端和服务端建立一个持久的连接,直到有一方主动关闭该连接。所以,现在服务器就知道有哪些用户正在连接了,这样通讯就变得相对容易了。 Socket.io支持及时、双向、基于事件的交流,可在不同平台、浏览器、设备上工作,可靠性和速度稳定。最典型的应用场景如:
Socket.io实际上是WebSocket的父集,Socket.io封装了WebSocket和轮询等方法,会根据情况选择方法来进行通讯。 Node.js提供了高效的服务端运行环境,但由于Browser对HTML5的支持不一,为了兼容所有浏览器,提供实时的用户体验,并为开发者提供客户端与服务端一致的编程体验,于是Socket.io诞生了。 # npm安装socket.op
$ npm install --save
Socket.io将WebSocket和Polling机制以及其它的实时通信方式封装成通用的接口,并在服务端实现了这些实时机制相应代码。这就是说,WebSocket仅仅是Socket.io实现实时通信的一个子集,那么Socket.io都实现了Polling中那些通信机制呢?
Socket.io 基本应用提供了基于事件的实时双向通讯,它同时提供了服务端和客户端的API。 服务端 服务端必须绑定一个
绑定
内部实例化并监听 // 实例化时传入端口
require('')(3000)
// 通过listen或attach函数绑定
let io = require('')
io.listen(3000);
// io.attach(3000);
Express框架中使用 let app = require('express');
let httpServer= require('http').Server(app);
let io = require('')(httpServer);
app.listen(3000);
KOA框架中使用
建立连接 当服务端和客户端连接成功时,服务端会监听到 /*客户端*/
<script src='http://cdn./stable/.js'></script>
<script>
// 引入成功后,可通过io()生成客户端所需的socket对象。
let socket = io('http://127.0.0.0:3000');
// socket.emmit()用户客户端向服务端发送消息,服务端与之对应的是socket.on()来接收信息。
socket.emmit('client message', {msg:'hi, server'});
// socket.on()用于接收服务端发来的消息
socket.on('connect', ()=>{
console.log('client connect server');
});
socket.on('disconnect', ()=>{
console.log('client disconnect');
});
</script>
/*服务端*/
// 服务端绑定HTTP服务器实例
let httpServer = require('http').Server();
let io = require('')(httpServer);
httpServer.listen(3000);
// 服务端监听连接状态:io的connection事件表示客户端与服务端成功建立连接,它接收一个回调函数,回调函数会接收一个socket参数。
io.on('connection', (socket)=>{
console.log('client connect server, ok!');
// io.emit()方法用于向服务端发送消息,参数1表示自定义的数据名,参数2表示需要配合事件传入的参数
io.emmit('server message', {msg:'client connect server success'});
// socket.broadcast.emmit()表示向除了自己以外的客户端发送消息
socket.broadcast.emmit('server message', {msg:'broadcast'});
// 监听断开连接状态:socket的disconnect事件表示客户端与服务端断开连接
socket.on('disconnect', ()=>{
console.log('connect disconnect');
});
// 与客户端对应的接收指定的消息
socket.on('client message', (data)=>{
cosnole.log(data);// hi server
});
socket.disconnect();
});
传输数据 服务端和客户端的socket是一个关联的
|
|
来自: liang1234_ > 《websocket》