本文主要对客户端和服务器通讯方式的发展进行小结。

网络通讯:从Ajax到Websocket的发展小结

参考文献:

远古时期

传统的服务器和客户端交互,是通过<form>表单来实现的。

Ajax时代

到后来出现了Ajax技术 ,就可以通过js来发起Ajax请求。

Ajax的readyState:

  • 0 - (未初始化)还没有调用send()方法
  • 1 - (载入)已调用send()方法,正在发送请求
  • 2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
  • 3 - (交互)正在解析响应内容
  • 4 - (完成)响应内容解析完成,可以在客户端调用了

对于一些C/S交互不是特别频繁的情况,问题不会太大;但是,如果是一些对实时性要求高、海量数据并发的应用来说,就不行了,比如常见的网页游戏、证券网站、RSS订阅推送、网页实时对话、打车软件等等。同样在客户端要呈现一些信息时,在服务器端很可能就要过时了。

后来出现了Comet技术,这是实际上是一种hack技术,通过它可以实现服务器推送(server push)。Comet技术的实现有2种方法:基于Ajax的长轮询(Long Polling)和流技术机制(利用iframe标签的长连接的特性)。

普通轮询(短轮询):浏览器定时向服务器发送请求,看有没有更新的数据,服务器收到请求后,会 【立刻发送】 响应,无论数据是否有效、是否有更新。

长轮询:浏览器定时向服务器发送请求,然后服务器 【一直保持连接打开,直到有数据可发送】 。发送完数据之后,浏览器关闭连接,然后浏览器又会发起一个新的请求。

Http流技术机制:流不同于上述两种轮询,因为它在页面的整个生命周期中只使用1个HTTP连接,具体来说,就是浏览器向服务器发起请求,而服务器保持连接打开,然后 【周期性地】 向浏览器发送数据。所有服务器语言都支持打印到输出缓存然后刷新(将输出缓存中的内容一次性全部发送到客户端)的功能。

但是无论是<form>、Ajax还是Comet,都无法解决一个根本的问题:由于它们都是基于http协议的,C/S之间的交互,必须要通过客户端发起请求。

WebSocket时代

后来W3C推出了WebSocket协议来解决这个问题。WebSocket协议是一个基于TCP协议的全双公工通讯协议,也就是说:客户端可以向服务器进行通讯,服务器也可以主动向客户端通讯。而且它支持文本数据以及二进制数据。

http2.0也有推送,那和websocket的区别是什么?

答:HTTP/2 只能推送静态资源,无法推送指定的信息。

WebSocket在通讯之前,要先进行握手,建立连接以后才可以进行双向通讯,而这个握手的过程是利用了HTTP协议来进行的。

WebSocket是怎么握手的?(参考文献

  1. 客户端发送Sec-WebSocket-Key给server
  2. 服务器收到Sec-WebSocket-Key后,加密并返回Sec-WebSocket-Accept给客户端
  3. 客户端收到Sec-WebSocket-Accept后,进行本地校验,若校验通过,则握手成功,客户端正式与服务器建立连接,然后开始进行数据传输。

WebSocket的readyState:

  • CONNECTING:值为0,表示正在连接。
  • OPEN:值为1,表示连接成功,可以通信了。
  • CLOSING:值为2,表示连接正在关闭。
  • CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

WebSocket的API常用的有这些:(参考MDN

  • onopen
  • onmessage
  • onclose
  • close
// 创建一个socket实例:
const socket = new WebSocket(ws://localhost:9093'); // 建立连接
// 打开socket
socket.onopen = (event) => {
    // 发送一个初始化消息
      socket.send('Hello Server!')
       // 服务器有响应数据触发
    socket.onmessage = (event) => {
        console.log('Client received a message',event)
    }
    // 出错时触发,并且会关闭连接。这时可以根据错误信息进行按需处理
    socket.onerror = (event) => {
          console.log('error')
    }
    // 监听Socket的关闭
    socket.onclose = (event) => {
        console.log('Client notified socket has closed',event)
    }
    // 关闭Socket
    socket.close(1000, 'closing normally')
}

socket.io

socket.io是将WebSocket和轮询(Polling)机制以及其他实时通讯方式封装成一个通过的接口,并在服务器端实现了这些实时机制的相应代码。

SSE

SSE:(服务器发送事件),创建到服务器的单向连接后,服务器就可以通过连接向客户端发送任意数量的数据。

WebSocket or SSE ?

WebSocket和SSE都是未来解决服务器推送问题的方案,在选择要使用哪一个的时候,要考虑一下问题:

  1. 是否有自由度简历和维护WebSocket服务器?因为WebSocket协议不同于HTTP,所以现有服务器不能用于WebSocket通信。SSE倒是通过常规HTTP通信,因此现有服务器可以满足需求。
  2. 到底需不需要双向通讯。如果用例只需要读取服务器数据(比如比赛成绩),那么SSE比较容易实现。如果用例必须双向通信(比如聊天室),那么WebSockets显然更好。
  3. 在不能使用WebSocket的时候,可以结合XHR和SSE来实现双向通信。