该系列博客是记录自己的学习笔记,一些资料片段来自网络,侵删
狗头声明,本文适用于:新手学习进步,老鸟回顾消遣
该系列以《图解HTTP》为基本路线,进行适当的拓展和补充,希望可以帮助大家。
(这篇笔记先记录着,后续本人有了更多实践后肯定优化补充)
WebSocket
WebSocket,即Web 浏览器与 Web 服务器之间全双工通信标准。
WebSocket也是一种协议,和HTTP平级
(1)、半双工的HTTP与全双工的WebSocket
HTTP的不足:
HTTP的“请求 - 应答”是一种“半双工”的通信模式,虽然可以双向收发数据,但同一时刻只能一个方向上有动作,传输效率低。更关键的一点,它是一种“被动”通信模式,服务器只能“被动”响应客户端的请求,无法主动向客户端发送数据。
所以,HTTP 难以应用在动态页面、即时消息、网络游戏等要求“实时通信”的领域。
在 WebSocket 出现之前,在浏览器环境里用 JavaScript 开发
实时 Web 应用很麻烦。因为浏览器是一个“受限的沙盒”,不能用 TCP/UDP,只有 HTTP 协议可用
所以就出现了很多“变通”的技术,“轮询”,即不停地向服务器发送 HTTP 请求,问有没有数据,有数据的话服务器就用响应报文回应。如果轮询的频率比较高,那么就可以近似地实现“实时通信”的效果。但轮询的缺点也很明显,反复发送无效查询请求耗费了大量的带宽和 CPU 资源,非常不经济。
WebSocket如何解决“实时问题”
WebSocket 是“全双工”的通信协议,与 TCP 一样,客户端和服务器都可以随时向对方发送数据,而不像HTTP协议一请求才能一响应
一旦Web服务器与客户端之间建立起WebSocket 协议的通信连接之后,所有的通信都依靠这个专用协议进行。通信过程中可互相发送JSON、XML、HTML或图片等任意格式的数据 由于是建立在HTTP 基础上的协议,因此连接的发起方仍是客户端,而一旦确立 WebSocket 通信连接,不论服务器还是客户端,任意一方都可直接向对方发送报文。
因此,一旦WebSocket连接后,服务器可以变得主动,只要后台有新的数据,就可以立即“推送”给客户端,不需要客户端主动请求去轮询,和使用轮询实现“实时通信”对比,WebSocket实现实时通信的开销小很多,提升的性能。
用到再学吧,面试就说自己了解WebSocket概念,暂时没有在真实项目中使用。
WebSocket的特点
WebSocket 没有使用 TCP 的“IP 地址 + 端口号”,而是延用了 HTTP 的 URI 格式,但开头的协议名不是“http”,引入的是两个新的名字:“ws”和“wss”(对应http,https),分别表示明文和加密的 WebSocket 协议。
WebSocket 的默认端口也选择了 80 和 443,因为现在互联网上的防火墙屏蔽了绝大多数的端口,只对 HTTP 的 80、443 端口“放行”,所以 WebSocket 就可以“伪装”成 HTTP 协议,比较容易地“穿透”防火墙,与服务器建立连接。
ws://www.chrono.com
ws://www.chrono.com:8080/srv
wss://www.chrono.com:445/im?user_id=xxx
如何建立起WebSocket连接
通用头部字段Upgrade
为了实现 WebSocket 通信,先要建立HTTP 连接,建立之后,需要完成一次“握手”(Handshaking)的步骤:
需要用到 HTTP 的通用字段Upgrade,告知服务器通信协议发生改变,以达到握手的目的。
这样就能绕过浏览器沙盒、网络防火墙等等限制(因为浏览器是一个“受限的沙盒”,不能用 TCP/UDP,只有 HTTP 协议可用)
Sec-WebSocket-Key字段内记录着握手过程中必不可少的键值。
Sec-WebSocket-Protocol字段内记录使用的子协议。
子协议按 WebSocket 协议标准在连接分开使用时,定义那些连接的名称。
这个HTTP请求告诉服务器要使用WebSocket协议来通信后,使命就结束了,服务器会返回状态码101 Switching Protocols 的响应。
然后就进行握手,握手成功后就建立WebSocket通信
WebSocket API
JavaScript可调用“The WebSocket API” (W3C标准制定) 内提供的 WebSocket 程序接口,以实现WebSocket协议下全双工通信
以下为调用WebSocketAPI,每50ms 发送一次数据的实例。
var socket = new WebSocket('ws://...');
socket.onopen = function() {
setInterval(function(){
if(socket.bufferedAmount == 0){
socket.send(getUpdateData());
}
}, 50);
};