新手小白研究一下websocket

WebSocket是双向的,在客户端-服务器通信的场景中使用的全双工协议,与HTTP不同,它以ws://wss://开头。它是一个有状态协议,这意味着客户端和服务器之间的连接将保持活动状态,直到被任何一方(客户端或服务器)终止。在通过客户端和服务器中的任何一方关闭连接之后,连接将从两端终止。

如果其中任何一方(客户端服务器)宕掉或主动关闭连接,则双方均将关闭连接。套接字的工作方式与HTTP的工作方式略有不同,状态代码101表示WebSocket中的交换协议。

原理

当客户端要和服务端建立 WebSocket 连接时,在客户端和服务器的握手过程中,客户端首先会向服务端发送一个 HTTP 请求,包含一个 Upgrade 请求头来告知服务端客户端想要建立一个 WebSocket 连接。

Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ************==
Sec-WebSocket-Version: **

Server正确接收后,会返回一个响应头:

Upgrade:websocket
Connnection: Upgrade
Sec-WebSocket-Accept: ******


和TCP、HTTP协议的关系

WebSocket是基于TCP的独立的协议。
和HTTP的唯一关联就是HTTP服务器需要发送一个“Upgrade”请求,即101 Switching Protocol到HTTP服务器,然后由服务器进行协议转换。

优点

  • 较少的控制开销,在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于 HTTP 请求每次都要携带完整的头部,此项开销显著减少了。
  • 更强的实时性,由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;
  • 长连接,保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  • 双向通信、更好的二进制支持。与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易被屏蔽,能通过各种 HTTP 代理服务器。

示例

后端使用node.js来hostwebsocket服务,使用ws这个三方库:https://github.com/websockets/ws
当然java和.net也都有很好的库支持,这里不做介绍。

Server端代码如下,记得装依赖包:

- express

- ws

'use strict';

const express = require('express');
const path = require('path');
const { createServer } = require('http');

const WebSocket = require('ws');

const app = express();
app.use(express.static(path.join(__dirname, '/public')));

const server = createServer(app);
const wss = new WebSocket.Server({ server });

// http
app.get('/api/word', function (req, res) {
  res.send({ message: 'hello - http' })
})

// ws
wss.on('connection', function (ws) {
  ws.send(JSON.stringify({ message: 'hello - websocket' }), function () {});

  ws.on('message', function (data, isBinary) {
    wss.clients.forEach(function each(client) {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(data, { binary: isBinary });
      }
    });
  });

  ws.on('open', function() {
    console.log('connected');
    ws.send({ message: 'hello - websocket - open' });
  });

  ws.on('close', function () {
    console.log('closed');
  });
});

server.listen(8080, function () {
  console.log('Listening on http://localhost:8080');
});

Client端代码如下

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width">
</head>
<body>
  <button id="connect">建立连接</button>
  <input id="message-text" type="text">
  <button id="send">发送</button>
  <button id="close">关闭</button>
  <div id="message-list"></div>
</body>
<script type="text/javascript">
  let ws;
  document.getElementById("connect").onclick = function () {
    ws = new WebSocket('ws://localhost:8080');

    ws.onopen = function(evt) {
      console.log('建立连接,状态:' + ws.readyState);
    };

    ws.onmessage = function(evt) {
      const data = JSON.parse(evt.data)
      console.log("状态:" + ws.readyState + ";服务端返回数据:", data);
      const list = document.getElementById("message-list");
      list.innerHTML = `${list.innerHTML} <p>${data.message}</p>`;
    };

    ws.onerror = function(evt) {
      console.log('发生错误,状态:' + ws.readyState);
    };  

    ws.onclose = function(evt) {
      console.log("连接关闭,状态:", ws.readyState);
    }; 
  }

  document.getElementById("send").onclick = function () {
    const val = document.getElementById("message-text").value
    const data = {
      message: val
    }
    const list = document.getElementById("message-list");
    list.innerHTML = `${list.innerHTML} <p>${data.message}</p>`;
    ws.send(JSON.stringify(data));
  }

  document.getElementById("close").onclick = function () {
    ws.close();
  }
</script>
</html>

效果如下:

打开两个浏览器,edge和chrome,两边都要先建立连接,然后就可以通信了。当一方关闭连接,另一方就收不到消息了。

相关标签:
  • websocket
0人点赞

发表评论

当前游客模式,请登陆发言

所有评论(0)