Socket.IO 2.0 (WebSocket)

Realtime API

📘

Socket.IO についての詳しい情報は Socket.IO 公式サイトをご参照ください。

エンドポイント

https://io.lightstream.bitflyer.com

🚧

  • websocket transport のみをサポートしています。多くのクライアントにおいて、これを明示的に指定する必要があります。
  • 接続には TLS 1.2 に対応したクライアントや、場合によっては明示的な設定が必要となります。

メソッド

  • auth - 認証要求をします
    • io.Socket#emit("auth", { /* (Auth Params) */ }, callbackFn)
    • (Auth Params) の内容は認証ページを参照してください
    • callbackFn 関数は必ず実装してください (第1引数でエラーを確認できます)
  • subscribe - チャンネルの購読を開始します
    • io.Socket#emit("subscribe", "(Channel Name)"[, callbackFn])
  • unsubscribe - チャンネルの購読を解除します
    • io.Socket#emit("unsubscribe", "(Channel Name)"[, callbackFn])

イベント

  • (Channel Name) - 購読しているチャンネルのメッセージが配信されます

利用例

// Node.js (JavaScript)
const crypto = require("crypto");
const io = require("socket.io-client");

const key = "{{ YOUR API KEY }}";
const secret = "{{ YOUR API SECRET }}";

const publicChannels = ["lightning_executions_BTC_JPY"];
const privateChannels = ["child_order_events", "parent_order_events"];

const socket = io("https://io.lightstream.bitflyer.com", {
    transports: ["websocket"] // specify explicitly
});

// connection handling
socket.on("connect", () => {
    // subscribe to the Public Channels
    for (const ch of publicChannels) {
        socket.emit("subscribe", ch, err => {
            if (err) {
                console.error(ch, "Subscribe Error:", err);
                return;
            }
            console.log(ch, "Subscribed.");
        });
    }

    // authentication parameters
    const now = Date.now();
    const nonce = crypto.randomBytes(16).toString("hex");
    const sign = crypto.createHmac("sha256", secret).update(`${now}${nonce}`).digest("hex");

    // request auth
    socket.emit("auth", {
        api_key: key,
        timestamp: now,
        nonce: nonce,
        signature: sign
    }, err => {
        if (err) {
            console.error("auth", "Authentication Error:", err);
            return;
        }
        console.log("auth", "Authenticated.");

        // subscribe to the Private Channels
        for (const ch of privateChannels) {
            socket.emit("subscribe", ch, err => {
                if (err) {
                    console.error(ch, "Subscribe Error:", err);
                    return;
                }
                console.log(ch, "Subscribed.");
            });
        }
    });
});

// set event listeners for all channels
for (const ch of [...publicChannels, ...privateChannels]) {
    socket.on(ch, message => {
        console.log(ch, message);
    });
}
// depends on io.socket socket.io-client and commons-codec
public class App 
{
    public static void main( String[] args )
    {
        final String[] PUBLIC_CHANNELS = {"lightning_ticker_BTC_JPY"};
        final String[] PRIVATE_CHANNELS = {"child_order_events"};
        final String secret = "{{ YOUR API KEY }}";
        final String key = "{{ YOUR API SECRET }}";

        IO.Options opts = new IO.Options();
        opts.transports = new String[] { WebSocket.NAME };

        final io.socket.client.Socket socket;
        try {
            socket = IO.socket("https://io.lightstream.bitflyer.com", opts);

            socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
                public void call(Object... objects) {
                    for (String channel : PUBLIC_CHANNELS) {
                        socket.emit("subscribe", channel);
                    }

                    final long now = System.currentTimeMillis();
                    final String nonce = generateNonce();
                    final String sign = hmacSha256(secret, now + nonce);

                    JSONObject obj = new JSONObject();
                    try {
                        obj.put("api_key", key);
                        obj.put("timestamp", now);
                        obj.put("nonce", nonce);
                        obj.put("signature", sign);
                    } catch (JSONException ignore){
                    }
                    socket.emit("auth", obj, new Ack() {
                        @Override
                        public void call(Object... args) {
                            if (args.length > 0 && args[0] != null) {
                                JSONObject error = (JSONObject) args[0];
                                System.out.println("auth error: " + error.toString());
                                socket.close();
                                return;
                            }
                            for (String channel : PRIVATE_CHANNELS) {
                                socket.emit("subscribe", channel);
                            }
                        }
                    });
                }
            });

            for (final String channel : PUBLIC_CHANNELS) {
                socket.on(channel, new Emitter.Listener() {
                    public void call(Object... objects) {
                        System.out.print(channel + " ");
                        System.out.println(objects[0]);
                    }
                });
            }
            for (final String channel : PRIVATE_CHANNELS) {
                socket.on(channel, new Emitter.Listener() {
                    public void call(Object... objects) {
                        System.out.print(channel + " ");
                        System.out.println(objects[0]);
                    }
                });
            }

            socket.connect();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    private static String generateNonce() {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[16];
        random.nextBytes(bytes);

        return new String(Hex.encodeHex(bytes));
    }

    private static String hmacSha256(String secret, String data) {
        try {
            final Mac mac = Mac.getInstance("HMacSHA256");
            mac.init(new SecretKeySpec(secret.getBytes(), "HMacSHA256"));
            final byte[] signBytes = mac.doFinal(data.getBytes());
            return new String(Hex.encodeHex(signBytes));
        } catch (NoSuchAlgorithmException ignored) {
        } catch (InvalidKeyException ignored) {
        }

        return "";
    }
}