如何从 Kite Connect 读取 WebSocket 数据并将其转换为 JSON 格式(Blob 到 JSON)?

How can I read WebSocket data and convert it to JSON format (Blob to JSON) from Kite Connect?

前言:我是websockets新手

连接到 WebSocket 后,我​​可以打开、关闭和接收消息,但我不知道如何读取传入数据。

我收到的消息如下所示:

MessageEvent {isTrusted: true, data: Blob, origin: "wss://ws.kite.trade", lastEventId: "", source: null, …}
          bubbles: false
          cancelBubble: false
          cancelable: false
          composed: false
          currentTarget: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          data: Blob {size: 48, type: ""}
          defaultPrevented: false
          eventPhase: 0
          isTrusted: true
          lastEventId: ""
          origin: "wss://ws.kite.trade"
          path: []
          ports: []
          returnValue: true
          source: null
          srcElement: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxxxxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          target: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxxxxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          timeStamp: 74468.20000000298
          type: "message"
          userActivation: null

e.data 看起来像:

Blob {size: 48, type: ""}
  size: 48
  type: ""
  [[Prototype]]: Blob
    arrayBuffer: ƒ arrayBuffer()
    size: (...)
    slice: ƒ slice()
    stream: ƒ stream()
    text: ƒ text()
    type: (...)
    constructor: ƒ Blob()
    Symbol(Symbol.toStringTag): "Blob"
    get size: ƒ size()
    get type: ƒ type()

这是我到目前为止在我的 React 应用程序中所做的:

CodeSandbox

import { useEffect, useRef } from "react";
import { Typography } from "@material-ui/core";

const DUMMY_HOLDINGS = [
  {
    tradingsymbol: "IRFC",
    instrument_token: 519425,
    last_price: 23.45
  },
  {
    tradingsymbol: "M&M",
    instrument_token: 519937,
    last_price: 755
  },
  {
    tradingsymbol: "SINTEXPLAST",
    instrument_token: 138407172,
    last_price: 5.65
  },
  {
    tradingsymbol: "ZOMATO",
    instrument_token: 1304833,
    last_price: 138.7
  },
  {
    tradingsymbol: "ZYDUSWELL",
    instrument_token: 136021764,
    last_price: 2170
  }
];

const SomeComponent = (props) => {
  const ACCESS_TOKEN = `xxxxxxxxxxxxxxxxx`; //redacted for obvious reasons
  const API_KEY = `xxxxxxxxxx`; //redacted for obvious reasons
  const ws_url = `wss://ws.kite.trade?api_key=${API_KEY}&access_token=${ACCESS_TOKEN}`;
  console.log(`WebSocket URL is: `, ws_url);

  const ws = useRef(null); // as a pointer to the websocket connection

  // The first useEffect gets the holdings if there are none, and
  // then creates a websocket connection
  useEffect(() => {
    // creates a new WebSocket
    ws.current = new WebSocket(ws_url);
    // the websocket requries one to "subscribe" to the instruments one wants
    // live data for, for each instrument it requires it's instrument_token
    // extracted from the DUMMY_ARRAY
    // refer to the docs for more info: https://kite.trade/docs/connect/v3/websocket/#request-structure
    const holdingInTokenArray = DUMMY_HOLDINGS.map(
      (holding) => holding.instrument_token
    );
    const message = { a: "subscribe", v: holdingInTokenArray };
    // Once the WS connection opens we send the request for the instruments subscribed as per the
    // docs in the message above
    ws.current.onopen = () => {
      console.log(`ws opened for ALL DUMMY_HOLDINGS`);
      console.log(message);
      ws.current.send(JSON.stringify(message));
    };
    ws.current.onclose = () => console.log(`ws closed for ALL DUMMY_HOLDINGS`);

    // Quick refresher: the useEffect return statement below runs before the useEffect hook, EXCEPT
    // EXCEPT for
    // 1. upon first render
    // 2. upon removal of DOM component
    return () => {
      ws.current.close();
    };
  }, [ws_url]);

  // This useEffect is for handling the onmessage WS event.
  // THIS IS WHERE MY PROBLEM BEGINS...
  useEffect(() => {
    // Simple guard statement
    if (!ws.current) return;

    // the onmessage event fires as expected, I just don't know how to read the e.data
    // which is supposed to contain the data I am looking for. I've read the docs
    // https://kite.trade/docs/connect/v3/websocket/#binary-market-data
    // tried .text() .arrayBuffer() but I can't make sense of the results.
    // My think my confusion is rooted in not knowing how to read Blob Data and converting
    // Blob into a format like the docs. Even if I just get an array of the Binary Data like
    // the docs I can handle it from there, I just don't know how to
    ws.current.onmessage = async (e) => {
      console.log(e.data);
      /**
       * e.data logs the following 
       MessageEvent {isTrusted: true, data: Blob, origin: "wss://ws.kite.trade", lastEventId: "", source: null, …}
          bubbles: false
          cancelBubble: false
          cancelable: false
          composed: false
          currentTarget: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          data: Blob {size: 48, type: ""}
          defaultPrevented: false
          eventPhase: 0
          isTrusted: true
          lastEventId: ""
          origin: "wss://ws.kite.trade"
          path: []
          ports: []
          returnValue: true
          source: null
          srcElement: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxxxxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          target: WebSocket {url: "wss://ws.kite.trade/?api_key=xxxxx&access_token=xxxxxxxx", readyState: 3, bufferedAmount: 0, onerror: null, onopen: ƒ, …}
          timeStamp: 74468.20000000298
          type: "message"
          userActivation: null
       */

      console.log(await e.data.text());
      const message = JSON.parse(e.data);
      console.log("e: ", message);
    };
  }, []);

  // Once I am able to read the returned WS data, I want to populate some of it below for each of the
  // instruments.
  return (
    <>
      {!DUMMY_HOLDINGS.length ? (
        <Typography>No Holdings Found</Typography>
      ) : (
        DUMMY_HOLDINGS.map((item) => {
          return (
            <div style={{ "box-shadow": "0 4px 8px 0 rgba(0,0,0,0.2)" }}>
              <h3>
                Stock : {item.tradingsymbol} ({item.instrument_token})
              </h3>
              <p>Last Price: {item.last_price}</p>
            </div>
          );
        })
      )}
    </>
  );
};

export default SomeComponent;

我只是不明白如何将数据从二进制文件中提取为我知道如何处理的东西。一段时间以来,我一直在无休止地挠头,我认为对于有 websockets 经验的人来说这很容易,请帮忙!

首先使用

将e.data响应中收到的Blob数据转换为ArrayBuffer
let t1 = await blob.arrayBuffer()
const a1 = new DataView(t1.slice(0,2)).getInt16() // number of packets
const b1 = new DataView(t1.slice(2,4)).getInt16() // bytes in packet
const c1_instrument_token = new DataView(t1.slice(4,8)).getInt32() // instrument token of packet 1
const c1_ltp = new DataView(t1.slice(8,12)).getInt32() // ltp of packet 1

等等关键是使用DataView提取底层值。我发誓我未来的自己会忘记我知道这一点并在某个时候读到这篇文章并感谢我过去的自己。希望这对其他人也有帮助

const text = await (new Response(e.data)).text();