WebSocket简介
谈到Web实时推送,就不得不说WebSocket。在WebSocket出现之前,很多网站为了实现实时推送技术,通常采用的方案是轮询(Polling)和Comet技术,Comet又可细分为两种实现方式,一种是长轮询机制,一种称为流技术,这两种方式实际上是对轮询技术的改进,这些方案带来很明显的缺点,需要由浏览器对服务器发出HTTP request,大量消耗服务器带宽和资源。面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并实现真正意义上的实时推送。
1. wbesocket是什么?
背景:WebSocket是HTML5出的东西(协议)
可以把 WebSocket 看成是 HTTP 协议为了支持长连接所打的一个大补丁,它和 HTTP 有一些共性,是为了解决 HTTP 本身无法解决的某些问题而做出的一个改良设计。WebSocket不是HTTP协议,HTTP只负责建立WebSocket连接。
WebSocket协议本质上是一个基于TCP的 独立的协议,能够在浏览器和服务器之间建立双向连接,以基于事件的方式,赋予浏览器实时通信能力。就是服务器端和客户端可以同时发送并响应请求,而不再像HTTP的请求和响应。
协议名为"ws",这意味着一个websocket连接地址会是这样的写法:ws://**。
websocket协议本质上是一个基于tcp的协议是服务器实现的。
客户端通过html5与服务器交互。http是不持续连接的,而websocket是。必须服务器支持websocket协议,才有效。对于不支持websocket服务的服务器,你客户端怎么写代码都没用。
WebSocket实战:
Web领域的实时推送技术,这种技术要达到的目的是让用户不需要刷新浏览器就可以获得实时更新。它有着广泛的应用场景,比如在线聊天室、在线客服系统、评论系统、WebIM等。
2.websocket解决了什么问题?
在了解这个之前我先来科普一下:
了解背景:
在以前 HTTP 协议中所谓的 **keep-alive connection** 是:
指在一次 TCP 连接中完成多个 HTTP 请求,但是对每个请求仍然要单独发 header;
所谓的 polling :
是指从客户端(一般就是浏览器)不断主动的向服务器发 HTTP 请求查询是否有新数据 。
这两种模式有一个共同的缺点:
就是除了真正的数据部分外,服务器和客户端还要大量交换 HTTP header,信息交换效率很低。
它们建立的“长连接”都是伪.长连接.
只不过好处是不需要对现有的 HTTP server 和浏览器架构做修改就能实现。
http long poll,或者ajax轮询不都可以实现实时信息传递
ajax轮询:
原理:让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
场景再现:
客户端(发请求,建立链接):啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端(发请求,建立链接):啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端(发请求,建立链接):啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端(发请求,建立链接):啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端(发请求,建立链接):啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) ---- loop
http long poll :
原理:long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,服务器就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
场景再现:
客户端(发请求,建立链接):啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
等等等。。。。。
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端(发请求,建立链接):啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性(服务端不能主动联系客户端,只能有客户端发起。)。
简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。
由此可以看出上面那种方式的缺陷:
非常消耗资源的:
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
所以在这种情况下出现了,Websocket出现了。
它解决了HTTP的这几个难题:
http协议缺点:
非持久性
同步有延迟
消耗资源
无状态协议。
被动性
另外websocket也解决:
可以用于绕过大多数防火墙的限制。
实现实时信息传递么。
websocket建立持久连接的实现原理:
通过第一个 HTTP request 建立了 TCP 连接之后,之后的交换数据都不需要再发 HTTP request了,使得这个长连接变成了一个真.长连接。但是不需要发送 HTTP header就能交换数据显然和原有的 HTTP 协议是有区>别的,所以它需要对服务器和客户端都进行升级才能实现。
在此基础上 WebSocket 还是一个双通道的连接,在同一个 TCP 连接上既可以发也可以收信息。此外还有 multiplexing 功能,几个不同的 URI 可以复用同一个 WebSocket 连接。这些都是原来的 HTTP 不能做到的。
当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
所以上面的情景可以做如下修改:
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)
那么为什么他会解决服务器上消耗资源的问题呢?
- 建立持久连接
其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(服务器)。本身接线员基本上速度是足够的,但是每次都卡在客服(服务器)了,老有客服(服务器)处理速度太慢。,导致客服(服务器)不够。
Websocket就解决了这样一个难题,建立后,可以直接跟接线员(Nginx)建立持久连接,有信息的时候客服(服务器)想办法通知接线员,然后接线员在统一转交给客户(客户端)。这样就可以解决客服处理速度过慢的问题了
- 只需要一次握手
传统的方式上,http要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输identity info(鉴别信息),来告诉服务端你是谁。
虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。
但是Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。
- 服务器主动推送信息
由客户主动询问,转换为服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。),没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的客服(Handler)了
3. websocket和HTTP协议的 联系 和 区别?
Websocket其实是一个新协议
WebSocket 和 HTTP 都是基于 TCP 的协议
TCP是传输层协议, WebSocket 和 HTTP是应用层协议
WebSocket 本质上跟 HTTP 完全不一样,WebSocket 协议和 HTTP 协议是两种不同的东西
只是为了兼容现有浏览器的握手规范而已,WebSocket 的握手是以 HTTP 的形式发起的,如果服务器或者代理不支持 WebSocket,它们会把这当做一个不认识的 HTTP 请求从而优雅地拒绝掉。
联系:
它们扯上关系是只是因为:
客户端开始建立 WebSocket 连接时要发送一个 header 标记了 Upgrade 的 HTTP 请求,表示请求协议升级。
(或者说借用了HTTP的协议来完成一部分握手)
服务器端做出响应的简便方法是:
直接在现有的 HTTP 服务器软件和现有的端口上实现 WebSocket 协议,重用现有代码(比如解析和认证这个 HTTP 请求。如果在 TCP 协议上实现,这两个功能就要重新实现),然后再回一个状态码为 101 的 HTTP 响应完成握手,再往后发送数据时就没 HTTP 的事了。
也就是说它是HTTP协议上的一种补充可以通过这样一张图理解 ,有交集,但是并不是全部。
和HTTP 的唯一关联是使用HTTP 协议的101状态码进行协议切换,使用的TCP 端口是80。
区别:
-
持久性:
HTTP协议:
HTTP是非持久的协议(长连接,循环连接的不算)
websocket协议:
Websocket是一个持久化的协议
-
生命周期:
HTTP的生命周期通过Request来界定,也就是一个Request 一个Response
在HTTP1.0中,这次HTTP请求就结束了。
在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。但是请记住 Request = Response
在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。
4.Websocket具体有什么优点 ?
他解决了web实时化的问题,相比传统http有如下好处:
- 实时交互
- 只需要建立一次连接(一个WEB客户端只建立一个TCP连接)
- 服务器能够主动推送内容(Websocket服务端可以推送(push)数据到web客户端.)
- 有更加轻量级的头,减少数据传送量
- 快速(延迟小,每条消息可以小到两个字节)
- 开发者友好(接口简单,并是熟悉的事件模型)
5. websocket的原理和应用
websocket的原理:
websocket通信协议实现的是基于浏览器的原生socket,这样原先只有在c/s模式下的大量开发模式都可以搬到web上来了,基本就是通过浏览器的支持在web上实现了与服务器端的socket通信。
WebSocket没有试图在HTTP之上模拟server推送,而是直接在TCP之上定义了帧协议,因此WebSocket能够支持双向的通信。
一旦取得 Web 服务器上的 Web Socket 连接之后,就可以通过调用 send() 方法从浏览器发送数据到服务器上,通过onmessage 事件处理程序从服务器接收数据到浏览器中。
下面是创建一个新的 WebSocket 对象的 API。
var Socket = new WebSocket(url, [protocal] );
第一个参数 url 用于指定要连接的 URL。第二个属性 - 端口是可选的,如果提供,就会指定一个服务器必须支持连接成功的子协议。
属性 | 描述 |
---|---|
Socket.readyState | 只读属性readyState表示连接的状态。有以下取值: |
0 表示连接尚未建立。 | |
1 表示连接已建立,可以进行通信。 | |
2 表示连接正在进行关闭握手。 | |
3 表示连接已经关闭或者连接不能打开。 |
WebSocket 事件
下面是 WebSocket 对象相关的事件。假定我们已经创建了上述的 Socket 对象,客户端通过websocket API提供的如下4个事件进行编程:
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 建立 socket 连接时触发这个事件。 |
message | Socket.onmessage | 客户端从服务器接收数据时触发。 |
error | Socket.onerror | 连接发生错误时触发。 |
close | Socket.onclose | 连接被关闭时触发。 |
WebSocket 方法
下面是 WebSocket 对象相关的方法。假定我们已经创建了上述的 Socket 对象:
方法 | 描述 |
---|---|
Socket.send() | send(data) 方法使用连接传输数据。 |
Socket.close() | close() 方法用于终止任何现有连接。 |
Websocket握手的实现
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
熟悉HTTP的可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。
对上面做出讲解:
多了下面的东西,这个就是Websocket的核心:
Upgrade: websocket
Connection: Upgrade
告诉Apache、Nginx等服务器:注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
#首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的
告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。
Sec-WebSocket-Protocol: chat, superchat
#Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。
简单理解:今晚我要服务A,别搞错啦~
Sec-WebSocket-Version: 13
#Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本)
在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有
然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!
HTTP/1.1 101 Switching Protocols
#这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket
Connection: Upgrade
#告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
#Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后Sec-WebSocket-Key。
#服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。
Sec-WebSocket-Protocol: chat
#后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。
依然是固定的,
然后,。至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。
使用WebSocket
在客户端使用websocket需要创建WebSocket对象,通过提供的open、send、message、close等方法实现创建、发送、监听信息、关闭连接。例如下面的代码:
if('WebSocket' in window){
// 创建websocket实例
var socket = new WebSocket('ws://localhost:8080');
//打开
socket.onopen = function(event)
{
// 发送
socket.send('I am the client and I\'m listening!');
// 监听
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 关闭监听
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 关闭
//socket.close() };
}else{
alert('本浏览器不支持WebSocket哦~');
}
现在chrome、firefox等浏览器都已经支持了websocket,而IE却没有。下面我们来简单说说服务器端对websocket的支持。
服务器端支持websocket的语言不少,而且都有相关的开源项目,例如php的。
socket.io
Socket.IO是一个开源的WebSocket库,包括了客户端的js和服务器端的nodejs。官方地址:http://socket.io
它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。Socket.IO支持以事件为基础的实时双向通讯,它可以工作在任何平台、浏览器或移动设备。
Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自动根据浏览器选择适合的通讯方式,从而让开发者可以聚焦到功能的实现而不是平台的兼容性,同时Socket.IO具有不错的稳定性和性能。
websocket 和 socket.io的区别
socket.io封装 了websocket,同时包含了其它的连接方式,比如Ajax。原因在于不是所有的浏览器 都支持websocket,通过socket.io的封装 ,你不用关心里面用了什么连接方式。你在任何浏览器 里都可以使用socket.io来建立异步 的连接。socket.io包含了服务端 和客户端的库,如果在[浏览器] 中使用了socket.io的js,服务端 也必须同样适用。如果你很清楚你需要的就是websocket,那可以直接使用websocket。
socket.io是一个WebSocket协议的实现,用它你可以进行websocket通信,这是应用层 node.js net.socket是系统socket接口,用它你可以操作linux socket,这是传输层
websocket协议本质上也是使用系统socket,它是把socket引入了http通信,也就是不使用80端口进行http通信。它的目的是建立全双工的连接,可以用来解决服务器客户端保持长连接的问题。
客户端使用socket.io
去github clone socket.io的最新版本,或者直接饮用使用socket.io的CDN服务:
<script src="http://cdn.socket.io/stable/socket.io.js"></script>
下面可以创建使用socket.io库来创建客户端js代码了:
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
socket.on是监听,收到服务器端发来的news的内容,则运行function,其中data就是请求回来的数据,socket.emit是发送消息给服务器端的方法。
在使用Socket.IO类库时,服务器端和客户端之间除了可以互相发送消息之外,也可以使用socket端口对象的emit方法,互相发送事件。
socket.emit(event,data,[callback])
event表示:参数值为一个用于指定事件名的字符串。
data参数值:代表该事件中携带的数据。这个数据就是要发送给对方的数据。数据可以是字符串,也可以是对象。
callback参数:值为一个参数,用于指定一个当对方确定接收到数据时调用的回调函数。
一方使用emit发送事件后,另一方可以使用on,或者once方法,对该事件进行监听。once和on不同的地方就是,once只监听一次,会在回调函数执行完毕后,取消监听。
socket.on(event,function(data,fn){})
socket.once(event,function(data,fn){})
再体会下emit的三个参数:首先是服务器端:
再是客户端:
房间
房间是Socket.IO提供的一个非常好用的功能。房间相当于为指定的一些客户端提供了一个命名空间,所有在房间里的广播和通信都不会影响到房间以外的客户端。
进入房间与离开房间
使用join()方法将socket加入房间:
io.on('connection', function(socket){
socket.join('some room');
});
使用leave()方法离开房间:
socket.leave('some room');
在房间中发送消息
在某个房间中发送消息:
io.to('some room').emit('some event');
to()方法用于在指定的房间中,对除了当前socket的其他socket发送消息。
socket.broadcast.to('game').emit('message','nice game');
in()方法用于在指定的房间中,为房间中的所有有socket发送消息。
io.sockets.in('game').emit('message','cool game');
当socket进入一个房间之后,可以通过以下两种方式在房间里广播消息:
7.怎么在不支持Websocket的客户端上使用Websocket ?
答案是:
不能
但是可以通过上面说的 long poll 和 ajax 轮询来 模拟出类似的效果