webSocket和Http
一、为什么出现webSocket?
我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
-
http不支持持久链接,http2.1只支持长连接,循环连接
-
答案很简单,因为 HTTP 协议有一个缺陷:通信只能由客户端发起。
-
缺陷:
-
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。
-
如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
-
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
二、简介
WebSocket 协议在2008年诞生,2011年成为国际标准。所有浏览器都已经支持了。HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于TCP传输协议,并复用HTTP的握手通道。
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
2.1、创建WebSocket
var Socket = new WebSocket(url, [protocol] );
- url:指定连接的 URL
- Protocol:是可选的,指定了可接受的子协议
2.2、WebSocket属性
-
Socket.readyState
只读属性 readyState 表示连接状态,可以是以下值:
- 0 - 表示连接尚未建立。
- 1 - 表示连接已建立,可以进行通信。
- 2 - 表示连接正在进行关闭。
- 3 - 表示连接已经关闭或者连接不能打开。
-
Socket.bufferedAmount
只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
2.3、WebSocket触发事件
以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:
- Socket.onopen:连接建立时触发
- Socket.onmessage:客户端接收服务端数据时触发
- Socket.onerror:通信发生错误时触发
- Socket.onclose:连接关闭时触发
2.4、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协议的握手请求中,多了几个东西。
Upgrade: websocket Connection: Upgrade
这个就是Websocket的核心了,告诉Apache、Nginx等服务器:发起的是Websocket协议,不是那个的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13
- Sec-WebSocket-Key:是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器进行验证是不是Websocket助理。
- Sec_WebSocket-Protocol :是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。
- Sec-WebSocket-Version:是告诉服务器所使用的Websocket Draft(协议版本)
-
服务端
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket Connection: Upgrade
依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。
然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。
后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。
2.5、WebSocket方法
以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:
- Socket.send():使用连接发送数据
- Socket.close():关闭连接
2.6、WebSocket实例
WebSocket 协议本质上是一个基于 TCP 的协议。
为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<script type="text/javascript">
function WebSocketTest()
{
var ws = new WebSocket("wss://echo.websocket.org");
ws.onopen = function(evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};
ws.onmessage = function(evt) {
console.log( "Received Message: " + evt.data);
ws.close();
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocket</a>
</div>
</body>
</html>
2.7、webSocket的特点
其他特点包括:
- 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少了。
- 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;即使是和Comet等类似的长轮询比较,其也能在短时间内更多次地传递数据。
- 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
- 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,可以更轻松地处理二进制内容。
- 可以支持扩展。Websocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
- 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
2.8、http和webSocket的选择
- 在webSocket没有出现之前的持久化连接怎么实现的?
- 采用轮询的机制实现,但是轮询有一个很致命的问题,不断的向服务端发送请求或导致占用大量的系统资源?这个问题如何解决呢?
- 在客户端设置超时时间(最好的实现是服务端知道客户端的具体超时时间)
- 若服务端没有发生数据改变或者其他更新操作,在接收客户端请求时,直接让客户端的请求变成阻塞状态(可以通过sleep等手段实现)
- 一旦服务端的数据发生变化或者有数据的更新则立马改变这个阻塞状态,在小于客户端设置的超时时间的前提下,响应客户端的请求。
- 这样的做法其实有webSocket里面的服务端能够与客户端的主动推送数据的能力(只不过借用阻塞这个状态实现服务端的主动)。
- 什么样的场景适合使用webSocket
- 因为webSocket是基于TCP协议的,因此需要维持连接状态,如果较大数量的客户端连接服务器,服务器的压力增加,可能造成服务器崩溃等情况,或者需要花费大量的资源去扩大服务器的承载能力。如腾讯如果采用webSocket协议实现,结果会怎么样。。。
- webSocket适用于报文数据段较小的应用场景,因为选用webSocket协议的初衷是为了保证浏览器或者客户端的实时性,因此在传输数据的时候不能是音频、视频等大型的数据文件。同时webSocket处理请求时的方式是先到先处理的模式,如果前面的报文过长,后面的报文较短,可能出现大量延时的情况。
- 总结一下就是需要保证实时性且保文较短的场景、不需要低延时的可以采用http,有一句话很经典“webSocket的http的一个补丁”