05月29, 2018

WebSocket 简介

WebSocket 简介

websocket的产生

我们都知道HTTP协议是一种请求-响应模型,表现在RequestResponse两个对象上,并且只能由客服端产生Request服务器被动的回复Response。但是在及时通讯的业务场景,这种通讯模型并不适用,因为服务端不能直接给客服端推送消息,所有只能由客服端不断的循环发送请求询问服务器数据是否有需要推送的消息,这种机制称为轮询。轮询机制因为要不断的发送请求,所以对服务器和客服端的性能都是一种很大的浪费,于是就产生了一种新的协议Websocket,它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话。 Websocket通讯

采用http握手升级

WebSocket 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议。WebSocket也是基于TCP/IP协议,在握手阶段和HTTP一致,在建立HTTP连接之后会发送一个协议升级请求。如下图:

http握手升级

我截取了这个协议升级的请求的详情:

GET http://192.168.10.202:8082/socket.io/?token=79e48a36ca0d4b968ba1a0070ebb6579&EIO=3&transport=websocket&sid=e3e6097f-ee44-4058-92f6-f1eb4034df37 HTTP/1.1
Host: 192.168.10.202:8082
Pragma: no-cache
Cache-Control: no-cache
Origin: http://localhost:8080
User-Agent: Chrome/55.0.2883.87
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Cookie: io=e3e6097f-ee44-4058-92f6-f1eb4034df37; JSESSIONID=AE0EC45B22A67DF9D56CA86451C6A3FA

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: i30qQR4hzdD/m8rZZZxRQA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

--------------------------------------------

HTTP/1.1 101 Switching Protocols
upgrade: websocket
connection: upgrade
sec-websocket-accept: QqgLPWkRbvFRDb4YXeVEO0WK4wY=
sec-websocket-extensions: permessage-deflate

请求里头多了几个请求头

  • Upgrade:websocket 表示升级到 websocket协议
  • connection: upgrade 协议升级
  • Sec-WebSocket-Key: U0GjCBUVuOVcIX7Asojx83o75sk= 客服产生的随机字符串,使用认证
  • Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits 客服端支持 websocket扩展

响应头:

  • upgrade: websocket 协议升级成 websocket
  • connection: upgrade 协议升级完成
  • sec-websocket-accept: QqgLPWkRbvFRDb4YXeVEO0WK4wY= 根据Sec-WebSocket-Key生成的信息
  • sec-websocket-extensions: permessage-deflate 服务器支持的websocket扩展

数据传输

websocket 连接建立以后,就可以彼此之间发送数据,不同于HTTP协议,websocket建立连接后传输数据不再需要握手,也不需要发送任何请求头,可以直接向对方发送数据(不同实现方式可能稍有不同)。发送消息之后也没有任何的响应,这样也会有一个问题就是,不知道对方是收到了消息没回复还是没收到消息。这种问题websocket客服端和服务端也提供了解决方案,就是发送ping/pong这个特殊的数据来维持心跳,但超过多少时间收不到心跳信号之后,连接会断开。

WebSocket 基本API

  1. 建立连接 var socket = WebSocket WebSocket(url,protocols); url: 服务端地址,必填 protocols: Webscoket子协议实现,可选参数

  2. 发送数据 socket.send(data); data:发送的数据,必填

  3. 关闭连接 socket.close(code,reason); code:代号,可选参数 reason:理由,可选参数

  4. 状态 readyState

常量 描述
CONNECTING 0 连接还没开启。
OPEN 1 连接已开启并准备好进行通信。
CLOSING 2 连接正在关闭的过程中。
CLOSED 3 连接已经关闭,或者连接无法建立。

WebSocket 事件

onclose

用于监听连接关闭事件监听器。当 WebSocket 对象的readyState 状态变为 CLOSED 时会触发该事件。这个监听器会接收一个叫close的 CloseEvent 对象。

onerror

当错误发生时用于监听error事件的事件监听器。会接受一个名为“error”的event对象。

onmessage

一个用于消息事件的事件监听器,这一事件当有消息到达的时候该事件会触发。这个Listener会被传入一个名为"message"的 MessageEvent 对象。

onopen

一个用于连接打开事件的事件监听器。当readyState的值变为 OPEN 的时候会触发该事件。该事件表明这个连接已经准备好接受和发送数据。这个监听器会接受一个名为"open"的事件对象。

一个基于nodejs的小例子

服务端

let ws = require("nodejs-websocket");
console.log("开始建立连接...");


let server = ws.createServer(function(conn){

  conn.on("conn",function (code, reason) {
    console.log("打开连接");
    conn.sendText("你好");
  });

  conn.on("text", function (str) {
    console.log("收到的信息为:"+str);
    conn.sendText("收到的信息为:"+str);
  });

  conn.on("close", function (code, reason) {
    console.log("关闭连接");
  });

  conn.on("error", function (code, reason) {
    console.log("异常关闭");
  });

});
server.listen(3000);
console.log("WebSocket建立完毕")

客户端

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket</title>
</head>
<body>
<div>
    <div id="msg">
        <div id="mess">连接中。。。</div>
    </div>
    <input id="sendMsg" width="200px">
    <button id="send" onclick="send()">发送</button>

</div>

<script>
  let msgDiv = document.getElementById("msg");
  if (window.WebSocket) {
    var ws = new WebSocket('ws://localhost:3000');

    ws.onopen = function (e) {
      appendMsg("连接服务器成功")
    }
    ws.onclose = function (e) {
      appendMsg("服务器关闭")
    }
    ws.onerror = function () {
      appendMsg("连接出错")
    }

    ws.onmessage = function (e) {
      appendMsg(e.data)
    }
  }

  function send() {
    let sendMsg = document.getElementById("sendMsg");
    ws.send(sendMsg.value);
    sendMsg.value = "";
  }

  function appendMsg(msg) {
    let element = document.createElement("div");
    element.innerHTML = msg;
    msgDiv.appendChild(element);
  }
</script>
</body>
</html>

Nginx 代理 Websocket 配置

Websocket服务部署时一般需要单独的端口,所以有可能需要使用nginx配置反向代理到80或者443端口


server {

    listen 80;
    server_name   chat.XXXXXX.com;

    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        proxy_connect_timeout 600;
        proxy_read_timeout 600;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://127.0.0.1:8888;
    }
}

本文链接:https://www.qiangshuidiyu.xin/post/websocket .html

-- EOF --

Comments