WebSocket和Socket

WebSocket和Socket

tags:WebSocket和Socketjavascript


引言:好多朋友想知道WebSocket和Socket的联系和区别,下面就是大家想要的php

先来一张以前收集的图,我看到这张图真的是笑了,当时还给我朋友门转发了一下,不知道你笑了没有。
html

看完上面的图,应该猜到了,他们之间也确实没有什么实质性的联系,固然除了名字有点相同,文章后面有名称的由来能够参考阅读java

Socket

英文socket的意思是插座,网络中的Socket是一个抽象的接口,能够理解为网络中链接的两端。一般被叫作套接字接口,其意义在对传输层进行封装屏蔽了传输层的复杂性。它并非一个协议,是为了你们更方便的使用传输层协议产生的一个抽象层。大部分的主流编程语言都提供socket函数。咱们拿php来举例说明nginx

<?php
1. socket_create() - 建立一个套接字
2. socket_accept() - Accepts a connection on a socket (接收)
3. socket_bind() - 给套接字绑定IP和端口号
4. socket_connect() - 和一个套接字创建链接 
5. socket_listen() - Listens for a connection on a socket (监听)
6. socket_last_error() - Returns the last error on the socket 
7. socket_strerror() - Return a string describing a socket error 
?>

咱们能够用这些函数来创建链接实现通讯功能。关于Socket咱们就说这些web

WebSocket

说道WebSocket了解过一些的人可能会以为有些高大上的感受,它的诞生还有些故事能够讲,大概是在为w3c放弃了html后,还有那么一群人不服气(不想放弃),想要继续推进html发展,同时他们也发展了一些其余的网络标准,而且被官方接收。而WebSocket就是其中一种,是为了建立一种双向通讯(全双工)的协议,来做为HTTP协议的一个替代者,以解决基于http上的长轮询等技术解决不了(或者解决的不那么优美)的问题。并且这厮一开始并不叫WebSocket,好像是叫webConnect之类的,最后是一位工程师提议说要么我们叫WebSocket吧,而后。。。。。,好了故事就这样,他既然是HTTP的替代者,咱们首先看一下它和HTTP(或者HTTP的长链接)的联系和区别。ajax

WebSocket和HTTP 1.1的联系

首先二者都是应用层协议,并且 WebSocket 在创建链接时,须要借用 http 的 101 switch protocol 来达到协议转换,为了创建一个 WebSocket 链接,客户端浏览器首先要向服务器发起一个HTTP请求,这个请求和一般的HTTP请求不一样,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"和"Connection: Upgrade"代表这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息而后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 链接就创建起来了,在创建链接后,就和HTTP没有关系了,双方就能够经过这个链接通道自由的传递信息。
固然,也有可能服务器不支持WebSocket,那就老老实实的用http吧,目前大部分浏览器和服务器都已支持WebSocket。编程

贴一段简单WebSocket客户端的js代码浏览器

<script type="text/javascript">
    //语法 var Socket = new WebSocket(url, [protocol] );
    var ws = new WebSocket("ws://localhost:6688/send");
    //链接创建时触发
    ws.onopen = function(evt) {
        console.log("Connection open ...");
        ws.send("Hello WebSockets!");
    };
    //接收消息时触发
    ws.onmessage = function(evt) {
        console.log("Received Message: " + evt.data);
        ws.close();
    };
    //关闭链接触发
    ws.onclose = function(evt) {
        console.log("Connection closed.");
    };
    //通讯发生错误时触发
    ws.onerror = function(evt) {
        console.log("Connection Error.");
    };
    //检查浏览器是否支持WebSocket
    if(typeof WebSocket != 'undefined'){
        alert("您的浏览器支持 WebSocket!");
    }else{
        // 浏览器不支持 WebSocket
        alert("您的浏览器不支持 WebSocket!");
    }
</script>

WebSocket和HTTP 1.1区别

咱们来看一下他的格式:安全

//一个WebSocket链接始于握手(handshake)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

对上面状态进行解释:
前两行跟HTTP的Request的起始行同样,而真正在WS的握手过程当中起到做用的是下面几个header域。

  • Upgrade:websocket
    upgrade是HTTP1.1中用于定义转换协议的header域。它表示要升级(转换)到某个协议(若是服务器支持的话)。

  • Connection:Upgrade 表示要进行升级协议

  • Sec-WebSocket-Key:用来发送给服务器过滤非预期的请求(好比手动填写header中的一些信息,但自己不想升级到WebSocket。这时候,因为Sec-WebSocket-Key和一些相关项被禁止手动设置,因此能够过滤掉出现非预期的状况)。

  • Origin:做安全使用,防止跨站攻击,浏览器通常会使用这个来标识原始域。

  • Sec-WebSocket-Protocol:客户端支持的子协议的列表。

  • Sec-WebSocket-Version:客户端支持的WS协议的版本。

//服务端应答handshake 101表示切换
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

上面是报文区别,还有一些其余的特性

  • WebSocket的链接必须是一个直接链接(这个我还须要仔细研究研究,还不是很透彻,若是有懂的朋友能够帮我理解理解将不胜感谢。回头我仔细研究一下通讯的方式和数据帧格式)。
  • WebSocket链接创建以后,通讯双方均可以在任什么时候刻向另外一方发送数据(即全双工,这是最主要的)。
  • WebSocket链接创建以后,数据的传输使用帧来传递,不一样于Request。

因为展开来说的话篇幅太长,你们也能够自行深刻了解。

WebSocket的全双工和HTTP

在HTTP中,一个Request对应着一个Response,早期的HTTP1.0每次的HTTP链接都须要打开一个TCP链接,在一个Request后,服务器产生一个应答Request,此次HTTP链接就结束了,同时关闭了TCP链接,重复的创建TCP链接是一种资源浪费,主动关闭TCP链接后还会出现time_wait状态,继续占用资源 一段时间(能够看上一篇文章TCP链接和 time_wait、close_waite
这种状况在HTTP1.1中进行了必定的改进,使得有一个keep-alive,也就是说,在一个HTTP链接中,能够发送多个Request,接收多个Response,能够减小创建和拆除TCP链接的次数,所以同时减小了time_wait状态的链接,可是,若是设置了keep-alive的超时时间好比nginx中是keepalive_timeout,一段时间没有通讯超时后服务器主动关闭链接也是可能形成服务器出现time_wait状态的,若是不设置超时时间也会形成必定的资源浪费(占用链接却不发送数据),因此怎么设置这个超时时间也很重要。

本质上HTTP1.1中虽然能够保持持久的链接,可是它依然不是全双工的,由于服务端是不能够主动给客户端发送消息的,ajax轮询的方式虽然能够达到WebSocket全双工的相似效果,可是会形成大量的资源浪费。

数据帧格式

0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+
  1. 从左到右,单位是比特。好比FIN、RSV一、RSV二、RSV3各占据1比特,opcode占据4比特。
  2. 内容包括了标识、操做代码、掩码、数据、数据长度等。(下一小节会展开)

各个标志位

参考上面的数据帧格式来讲一下各个字段的含义,重要字段加粗

  • FIN:(finish)1个比特。
    若是是1,表示这是消息的最后一个分片(fragment),若是是0,表示不是最后一个分片。

  • RSV1, RSV2, RSV3:各占1个比特。
    若不采用WebSocket扩展这里必须为0。当客户端、服务端协商采用WebSocket扩展时,这三个标志位能够非0,且值的含义由扩展进行定义。若是出现非零的值,且并无采用WebSocket扩展,链接出错。

  • Opcode: 4个比特。

    操做代码,定义了对“负载数据”的解释,Opcode的值决定了应该如何解析后续的数据载荷(data payload)。若是收到一个未知的操做码,接收端点应该断开链接(fail the connection)。可选的操做代码以下:

    %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
    %x1:表示这是一个文本帧(frame)
    %x2:表示这是一个二进制帧(frame)
    %x3-7:保留的操做代码,用于后续定义的非控制帧,(通常协议中都会预留出一些码用于扩展)。
    %x8:表示链接断开/关闭。
    %x9:表示这是一个ping操做。
    %xA:表示这是一个pong操做。
    %xB-F:保留的操做代码,用于后续定义的控制帧(通常协议中都会预留出一些码用于扩展)。

  • Mask: 1个比特。
    表示是否要对数据载荷进行掩码操做。从客户端向服务端发送数据时,须要对数据进行掩码操做,Mask须要为1,masking-key(掩码键)字段存在值;从服务端向客户端发送数据时,不须要对数据进行掩码操做,Mask须要为0。若是服务端接收到的数据没有进行过掩码操做,服务端须要断开链接。

  • Payload length:数据载荷的长度,单位是字节。为7位,或7+16位,或1+64位。

    假设Payload length的值为x,若是
    x为0~126:数据的长度为x字节。
    x为126:后续2个字节表明一个16位的无符号整数,该无符号整数的值为数据的长度。
    x为127:后续8个字节表明一个64位的无符号整数(最高位为0),该无符号整数的值为数据的长度。
    上面这种定义负载长度方式是一种网络协议中经常使用的方法,能够实现灵活扩展的数据长度定义。

  • Masking-key:0或4字节(32位)
    全部从客户端传送到服务端的数据帧,数据载荷都进行了掩码操做,Mask为1,且携带了4字节的Masking-key。若是Mask为0,则没有Masking-key。

  • Payload data:(载荷数据 x+y 字节)
    载荷数据:包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。载荷数据的长度,不包括mask key的长度。

  • Extension data(扩展数据 x 字节):
    若是没有协商使用扩展的话,扩展数据数据为0字节。全部的扩展都必须声明扩展数据的长度,或者能够如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。若是扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。

  • Application data(应用数据 y 字节):
    任意的应用数据在扩展数据以后(若是存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就获得应用数据的长度。

Ping Pang 心跳

对于长时间没有数据交互的链接,会浪费链接资源。但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍须要保持链接。这个时候,能够采用心跳来实现。(不知道心跳为何是ping、pang)

  1. ping
  • Ping帧操做码(opcode)0x9。能够包含“应用数据
  • 当收到一个Ping帧时,接收方必须在响应中发送一个Pong帧,除非它早已接收到一个关闭帧。它应该尽量快地以Pong帧响应。(也用来验证远程端点是否可响应)
  1. Pong
  • Pong帧操做码(opcode)0x9。
  • 一个Pong帧必须携和被响应的Ping帧中相同的数据
  • 若是再一个ping到达服务端,服务端还没有响应前,由到达同源的ping帧,则能够只响应最新的ping帧,
  • 未收到ping也能够发送一个Pong帧。这个充当单向的心跳(heartbeat),另外一方不须要响应。
相关文章
相关标签/搜索