WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
传统的HTTP协议是一个请求-响应协议,浏览器不主动请求,服务器是没法主动发数据给浏览器的。
Ajax 轮询
浏览器通过Javascript启动一个定时器,然后以固定的间隔给服务器发请求,询问服务器有没有新消息。
- 实时性不够
- 频繁的请求会给服务器带来极大的压力。
服务器反推
本质上也是轮询,但是在没有消息的情况下,服务器先拖一段时间,等到有消息了再回复。暂时地解决了实时性问题。
- 以多线程模式运行的服务器会让大部分线程大部分时间都处于挂起状态,极大地浪费服务器资源。
- 一个HTTP连接在长时间没有数据传输的情况下,链路上的任何一个网关都可能关闭这个连接。 长期占用连接,丧失了无状态高并发的特点。
WebSocket并不是全新的协议,而是利用了HTTP协议来建立TCP连接。
1 请求
WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求。
格式如下:
WebSocket请求和普通的HTTP请求不同:
- GET请求的地址不是类似/path/,而是以ws://开头的地址;
- 请求头Upgrade: websocket和Connection: Upgrade表示这个连接将要被转换为WebSocket连接;
- Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据;
- Sec-WebSocket-Version指定了WebSocket的协议版本。
2 响应
服务器如果接受该请求,就会返回如下响应:
响应代码101表示本次连接的HTTP协议即将被更改,更改后的协议就是Upgrade: websocket指定的WebSocket协议
WebSocket、HTTP 与 TCP 区别
HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的。 所以连接和断开,都要遵循 TCP 协议中的三次握手和四次握手 ,只是在连接之后发送的内容不同,或者是断开的时间不同。
对于 WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输,与 HTTP 无关了。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。
WebSocket 则不同,它是一个完整的 应用层协议,包含一套标准的 API。
从使用上来说,WebSocket 更易用,而 Socket 更灵活。
WebSocket API 是 HTML5 标准的一部分, 但这并不代表 WebSocket 一定要用在 HTML 中,或者只能在基于浏览器的应用程序中使用。
注意事项
长连接应用必须加心跳,否则连接可能由于长时间未通讯被路由节点强行断开。
- 客户端定时给服务端发送点数据,防止连接由于长时间没有通讯而被某些节点的防火墙关闭导致连接断开的情况。
- 服务端可以通过心跳来判断客户端是否在线,如果客户端在规定时间内没有发来任何数据,就认为客户端下线。这样可以检测到客户端由于极端情况(断电、断网等)下线的事件。
心跳间隔建议值:
建议客户端发送心跳间隔小于60秒,比如55秒。
1 属性
Socket.readyState
只读属性 readyState 表示连接状态,可以是以下值:
Socket.bufferedAmount
只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
2 事件
3 方法
参考: https://www.liaoxuefeng.com/wiki/1022910821149312/1103303693824096
服务端代码:
这里采用php方式来进行演示,其他语言也是类似,不在叙述。
客户端代码: