一、前言
之前做了一个Web
小项目,需要实现后端持续给前端推送消息的功能,当时最开始使用的是轮询实现,但是效率太低,对资源消耗也大。之后为了解决这个问题,上网查阅资料后,改用了WebSocket
实现,前后端直接建立全双工的连接传递消息。但是当时只是学习了一下怎么使用,没有具体研究WebSocket
是什么,以及实现原理。这段时间花了点时间重新了解了一下,这篇博客就来简单介绍一下。
二、正文
2.1 什么是WebSocket
WebSocket
和HTTP
一样,是一个应用层的网络协议,它的作用是可以使客户端和服务器在应用层建立一个全双工的连接,然后通过这个连接,客户端和服务器就可以互相传递数据。有的人可能会问,HTTP
不是也能实现客户端和服务器传递数据,为什么还需要WebSocket
呢?下面我就来对WebSocket
以及HTTP
做一个对比。
2.2 WebSocket、HTTP的区别与联系
首先来说HTTP
。我们知道,HTTP
是一个无状态的协议,也就是说,在服务器端并不会记录发送请求的客户端的身份,这也就意味着HTTP
服务器不会主动向客户端发送消息。事实也确实如此,HTTP
协议实现的是请求-响应模型,只有当客户端向服务器发送请求,服务器才会向客户端回送数据。熟悉HTTP
协议的应该知道,HTTP
连接分持续连接和非持续连接,对于使用非持续连接的HTTP
客户端,每一次连接只会有一次请求;而对于持续连接,则一个连接可以有多个请求,但是这个连接也是有时效的,几十秒或者几秒后这个连接就会被释放。服务器不会主动发送消息,意味着如果我们需要实现一个类似消息推送的功能(比如聊天室),则需要客户端每隔一段时间向服务器发出一次请求,询问是否有自己的消息,这就是轮询。而由于HTTP
连接维持时间较短,意味着可能客户端的每一次询问,都要重新建立一个HTTP
连接,这不仅效率低下,而且十分消耗资源。正因为如此,才需要WebSocket
。
WebSocket
正是为了解决这样的问题而被发明出来。WebSocket
是一个应用层的协议,它依靠HTTP
实现连接前的握手,而底层依赖TCP
协议传递消息。在建立连接阶段,客户端依靠HTTP
连接,与服务器进行一次握手,当握手完成后,WebSocket
连接便建立完成,这之后客户端就可以与服务器进行通信了。需要强调的是,WebSocket
依赖TCP
进行数据传输,这也就意味着这是一个全双工的连接,即服务器可以主动向客户端发送消息,而不需要客户端的请求;客户端也可以自由的向服务器发送消息。更重要的是,TCP
是一个持久的连接,而基于它的WebSocket
自然也是一样的,只要两端没有发起断开连接的请求,且连接没有长时间闲置,则在不发送异常的情况下,这个连接将一直存在。这就很好地解决了轮询重复建立连接的弊端。
2.3 WebSocket建立连接的过程
下面我们具体来说一说WebSocket
如何建立连接。前面说过,WebSocket
基于HTTP
进行握手从而建立连接,使用HTTP
协议是为了兼容服务于HTTP
的Web
服务器以及中间代理服务器。下面我就来详细说一说握手的三个阶段。
(1)客户端向服务器发送HTTP请求报文
握手的第一个阶段:客户端与服务器建立HTTP
连接,并发送请求报文,请求报文的格式如下:
GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: HTTP://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
可以看到,这个请求是一个GET
请求,请求中必须包含这些首部信息:
- Host:需要建立连接的服务器名称;
- Connection:必须包含此首部,且值必须为
Upgrade
,表明这是一个协议升级请求; - Upgrade:必须包含此首部,且值必须为
WebSocket
,表明当前连接请求升级为WebSocket
连接; - Origin:如果这个请求是从浏览器发出的,那么还必须带有
Origin
请求头; - Sec-WebSocket-Key:这是浏览器随机生成的值,用来验证服务器身份;
- Sec-WebSocket-Version:这个表示的是
WebSocket
的版本号码;
(2)服务器接收请求,并响应
握手第二个阶段:服务器接收到了客户端发来的协议升级请求,会先判断请求报文是否合法,若没有什么异常,则会给客户端发送响应,响应报文如下:
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
需要注意一点,响应报文的状态码是101
,101
状态码的含义是:服务器正在根据客户端的指定,将协议切换为请求报文中Upgrade
指定的协议。下面我们来说一说响应报文中的首部:
- Connection:值和请求报文中一致,表明这是一个协议升级请求的响应报文;
- Upgrade:值为
WebSocket
,表明这个响应报文,响应的协议升级请求,是希望升级为WebSocket
协议; - Sec-WebSocket-Accept:这个值是服务器接收到客户端发来的
Sec-WebSocket-Key
值后,经过加密计算出来的值,用来向客户端表明自己的身份;
(3)客户端验证服务器身份
客户端接收到服务器发回来的响应报文后,将对响应报文进行校验:
- 首先客户端将检测响应的状态码是否为
101
,若不是,表明协议升级失败,连接自然无法建立; - 若状态码正常,则接着检测
Sec-WebSocket-Accept
的值。客户端使用相同的加密方式,对Sec-WebSocket-Key
进行计算,若计算的结果与服务器发来的Sec-WebSocket-Accept
的值一致,表明服务器身份没有问题,于是连接成功建立,否则放弃建立连接;
连接建立后,客户端和服务器就可以基于TCP
,进行全双工的通信了。
三、总结
上面的内容对WebSocket
的机制做了一个大致的介绍,相信对于看完之后会对WebSocket
的工作机制有一个大致的了解。以上内容描述的比较简单,若有补充或者发现错误,欢迎指正。关于WebSocket
的详细介绍,可以参考规范文档【RFC6455】:HTTPs://tools.ietf.org/html/rfc6455#page-41。