一、前言
众所周知,当我们想要访问某网页时,浏览器输入网址后按下回车就能跳转到网页。
从HTTP协议的角度来说,这是一次客户端发出请求,服务器接收请求并做出响应的过程。这种客户端请求,服务器响应的方式已经能满足大部分网页功能。
但是你有没有发现,这种情况下服务器从来不会主动向客户端发送数据;就像你暗恋某个女生一样,你不主动,她从来不会主动找你。
二、HTTP的轮询
哈哈哈,言归正传。
假如你正在访问一个时效性比较强的网页时,如果你不主动刷新页面,看到的就一直是现有的内容,刷新后才能看到最新消息。那么如果我不想刷新,有没有办法也能看到最新消息呢。
当然是有的了,比如HTTP轮询。进入网页后,不断向服务器发送获取数据的请求,那么只要服务器有了最新的数据马上就能看到了。
但是这种方式需要不停地向服务器发送HTTP请求,造成的影响就是消耗网络带宽、增加服务器的负担等。
那么有没有解决办法呢,答案是当然有啦。
三、长轮询
HTTP给服务器发送请求后,如果服务器在一定时间内未响应,比如说2s内未响应,那么就认为此次请求超时。
那么如果我们给请求超时时间设置的大一点,比如设置60s,那么这60s内只要服务器有了最新数据就马上返回给客户端;如果超时,那就再次发送请求。
除了获取最新数据场景外,像扫码登录场景也可以采用长轮询方式,只要用户扫码确认登录则立即响应,如果没有扫码那就再次轮询。
其实不管是不断轮询还是长轮询,其本质还是客户端请求服务器响应的方式获取数据;对于以上简单场景还可以使用,但是对于向实时性要求非常强的场景,如游戏等,这种方式还是存在一定的不足。
四、研究本质寻找解决办法
我们知道,目前使用最广泛的HTTP/1.1是基于TCP协议的;客户端请求服务端响应,在这个过程中其实只有一方能够主动发送数据,也就是半双工模式。
而我们知道TCP是双方连接的,也就是说同一时间内双方都可以发送数据,也就是全双工模式。
这里我们就得好好批判一下HTTP了,你基于TCP不扩展点啥也就算了,还把原本的全双工整成了半双工;唉,有点一代不如一代的感觉了。
但这真的也不能怪HTTP的设计者,毕竟设计之初考虑的是能看到网页文本效果就可以了;而且那时也不知道游戏会变得这么火,所以这种客户端和服务端都需要主动发送大量数据的场景也就没有考虑。
所以,为了支持这种场景就需要设计一种新的基于TCP且支持全双工模式的协议,也就是WebSocket。
五、详解WebSocket
1. 概念和特点
基于TCP/IP协议,是一种新的通信协议;支持双向通信,有状态。
应用在浏览器上的Socket(是Socket模型接口的实现),Socket是一个网络通信接口(通信规范)。
与HTTP协议有着良好的兼容性,默认端口也是80和443,且建立连接阶段需要采用HTTP协议。
数据格式较为轻量,性能开销小,通信效率高;支持文本数据,也支持二进制数据。
没有同源限制,客户端可以和任意服务器进行通信;协议标识是ws(如果加密为wss),服务器网址是URL。
2. 如何建立连接
1)客户端和服务器通过TCP三次握手建立连接。
2)客户端采用HTTP通信,但是需要在HTTP请求头中携带特殊头部字段,为了升级为WebSocket协议。如下所示:
# 请求头部分
# [请求方式] [资源路径] [版本]
GET /xxx HTTP/1.1
# 主机。
Host: server.example.com
# 协议升级。
Upgrade: websocket
# 连接状态。
Connection: Upgrade
# websocket客户端随机生成的base64字符串。
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
# websocket协议的子协议,自定义字符,可以理解为频道。
Sec-WebSocket-Protocol: chat, superchat
# websocket协议的版本是13。
Sec-WebSocket-Version: 13
3)服务器收到请求且支持升级为WebSocket协议时,通过某种算法将客户端请求头中随机生成的base64字符串变成另一段字符串,放在响应头中返回给客户端。如下所示:
# 响应头部分
# [版本] [状态码]
HTTP/1.1 101 Switching Protocols
# 协议升级。
Upgrade: websocket
# 连接状态。
Connection: Upgrade
# WebSocket服务端根据Sec-WebSocket-Key生成的随机字符串。
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
# WebSocket协议的子协议,自定义字符,可以理解为频道。
Sec-WebSocket-Protocol: chat
注:响应状态码101表示协议切换,它是HTTP协议的响应状态码。Upgrade字段仅限HTTP/1.1版本,不适合HTTP/2.0版本。
4)客户端收到响应后先使用同样的算法将请求头中随机生成的base64字符串转变成另一段字符串,然后与响应头中传回的字符串进行对比,如果一致则验证通过。
5)通过两次HTTP握手,WebSocket连接建立成功,后面就可以使用WebSocket数据格式进行通信了。
3. 适用场景
因为WebSocket完美的继承了TCP的全双工能力,所以它适用于客户端和服务器频繁交互的场景,如游戏、对话聊天室等。
像我们平时玩游戏的时候,怪兽给你一拳所造成的伤害、减速等数据,都需要服务器主动推送给客户端(玩家),客户端获得数据后显示效果。