binance websocket 没有响应 ping

binance websocket not responding to ping

使用 gorilla/websocket 我拨通了 binance websocket 端点,成功无误。在连接上设置 pong 处理程序后,我编写了一个 ping 控制消息并等待 pong 到达 pong 处理程序,这似乎从未发生过。我使用一个通道、一个带超时的上下文和一个 select 块来检查乒乓球是否到达。

代码:

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/gorilla/websocket"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    conn, resp, err := websocket.DefaultDialer.DialContext(ctx, "wss://stream.binance.com:9443/ws", nil)
    if resp != nil {
        log.Printf("status: %s", resp.Status)
    }

    if err != nil {
        panic(err)
    }

    pong := make(chan struct{})
    conn.SetPongHandler(func(appData string) error {
        log.Println(appData)

        pong <- struct{}{}
        return nil
    })

    if err := conn.WriteControl(websocket.PingMessage, []byte("Hello, world!"), time.Now().Add(5*time.Second)); err != nil {
        panic(err)
    }

    select {
    case <-ctx.Done():
        panic(fmt.Errorf("pong wait: %w", ctx.Err()))
    case <-pong:
    }
}

输出:

$ go run ./cmd/ping
2022/02/07 20:01:23 status: 101 Switching Protocols
panic: pong wait: context deadline exceeded

goroutine 1 [running]:
main.main()
        /workspaces/yatgo/cmd/ping/ping.go:39 +0x2ba
exit status 2

根据rfc6455, section-5.5.2

5.5.2. Ping

The Ping frame contains an opcode of 0x9.

A Ping frame MAY include "Application data".

Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response, unless it already received a Close frame. It SHOULD
respond with Pong frame as soon as is practical. Pong frames are
discussed in Section 5.5.3.

An endpoint MAY send a Ping frame any time after the connection is established and before the connection is closed.

NOTE: A Ping frame may serve either as a keepalive or as a means to verify that the remote endpoint is still responsive.

我有点希望这能奏效。 Binance websocket API limits doc 确实提到了 ping 消息:

WebSocket connections have a limit of 5 incoming messages per second. A message is considered:

  • A PING frame

所以我想知道:

  1. 我的代码有问题吗?
  2. 或者 binance 不遵守 RFC6455?

Gorilla Websocket documentation 说:

The application must read the connection to process close, ping and pong messages sent from the peer. If the application is not otherwise interested in messages from the peer, then the application should start a goroutine to read and discard messages from the peer.

通过启动 goroutine 来读取 select 语句之前的连接来修复应用程序:

go func() {
    defer cancel()
    for {
        if _, _, err := conn.NextReader(); err != nil {
            fmt.Println(err)
            return
        }
    }
}()

select {
⋮

这是对问题中显示的应用程序的修复。如果您的实际应用程序在循环中从连接读取数据,那么您不应该添加此处显示的 goroutine。应用程序应使用一个读取循环来处理控制和数据消息。