Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[Bug] WebSocket Streams: after a connection drops, resubscribing to the same stream on the same client sends nothing

Aperta Adatta ai principianti
#576 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
82/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
python
Ambito
api, backend

Direzione di ricerca

Leggi common/src/binance_common/websocket.py, in particolare _discard_connection() e _forget_connection_streams(), poi esegui la riproduzione descritta nell’issue contro un server WebSocket locale. Verifica che, dopo la caduta della connessione, una nuova sottoscrizione con lo stesso client invii SUBSCRIBE e consegni l’aggiornamento di ripristino. L’issue suggerisce di dimenticare gli stream della connessione morta alla fine di _discard_connection().

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Follow-up to #570, which common v4.6.0 fixed for two clients in one process. With a single client, one case from the same stream map remains: after a stream connection drops unexpectedly, subscribing to the same stream again on a new connection does nothing. No SUBSCRIBE is sent, nothing is raised, nothing is logged about the skipped stream, and no messages arrive, so the stream looks like a quiet market. Recovering through a new Spot client works in 4.6.0.

When the server closes the connection or the TCP connection drops, the receive loop fires close and calls _discard_connection() (lines 414, 421 and 428), which removes the connection from the pool (line 497). The client's stream_connections still maps the stream to the dead connection, because _forget_connection_streams() runs only after a failed planned reconnect (line 907) and on a user-initiated close (line 1138). On the next subscribe, subscribe() filters the stream out as already registered (lines 1288-1292), and on() attaches the callback to the dead connection (lines 1361-1367). Spot.websocket_streams is cached, and create_connection() on it returns the same instance, so the new connection shares the stale map. Line numbers are from common/src/binance_common/websocket.py at a54ec0b, which matches the 4.6.0 release.

Reproduce

binance-sdk-spot==13.2.0, binance-common==4.6.0, aiohttp==3.14.5, Python 3.14.8. A local aiohttp server drops the first connection's TCP without a close frame. The client then opens a new connection on the same Spot client and subscribes to the same stream again. A close frame (1001) in place of the TCP drop gives the same result.

Reproduction script and output
import asyncio, json, logging
from aiohttp import web
from binance_common.configuration import ConfigurationWebSocketStreams
from binance_sdk_spot.spot import Spot
logging.disable(logging.CRITICAL)
conns, subs = [], []
async def handler(request):
    ws = web.WebSocketResponse(); await ws.prepare(request)
    conns.append(request); conn = len(conns)
    async for msg in ws:
        d = json.loads(msg.data)
        if d["method"] == "SUBSCRIBE":
            subs.append((conn, d["params"]))
            await ws.send_json({"id": d["id"], "result": None})
            for s in d["params"]:
                await ws.send_json({"stream": s, "data": {"e": "depthUpdate", "E": 1, "s": "BNBUSDT", "U": conn, "u": conn, "b": [], "a": []}})
            if conn == 1:
                await asyncio.sleep(0.2)
                request.transport.abort()  # the TCP connection drops, no close frame
    return ws
async def main():
    app = web.Application(); app.router.add_get("/stream", handler)
    r = web.AppRunner(app); await r.setup(); await web.TCPSite(r, "127.0.0.1", 18767).start()
    client = Spot(config_ws_streams=ConfigurationWebSocketStreams(stream_url="ws://127.0.0.1:18767/stream"))
    got, events = [], []
    ws = await client.websocket_streams.create_connection()
    ws.on_connection("close", lambda *a: events.append("close"))
    (await ws.diff_book_depth(symbol="bnbusdt")).on("message", lambda m: got.append(("first", m.U)))
    await asyncio.sleep(0.5)
    # recover: a new connection on the same client, same stream
    ws2 = await client.websocket_streams.create_connection()
    (await ws2.diff_book_depth(symbol="bnbusdt")).on("message", lambda m: got.append(("recovery", m.U)))
    await asyncio.sleep(0.5)
    print("same instance:", ws2 is ws)
    print("connections accepted by the server:", len(conns))
    print("connection events:", events)
    print("SUBSCRIBE frames received (connection, params):", subs)
    print("messages delivered:", got)
    await ws2.close_connection(close_session=True); await r.cleanup()
asyncio.run(main())

Output:

same instance: True
connections accepted by the server: 2
connection events: ['close']
SUBSCRIBE frames received (connection, params): [(1, ['bnbusdt@depth'])]
messages delivered: [('first', 1)]

Expected

The second connection sends SUBSCRIBE for bnbusdt@depth, and the recovery callback receives the second connection's update.

Suggested fix

Call self._forget_connection_streams(connection) at the end of _discard_connection(), after its early return for planned reconnects. Its docstring already describes this case: "A connection that is gone for good must not be left in the stream maps, or a later subscribe would treat its streams as still live and silently skip them." With that call patched in, the script above shows one SUBSCRIBE per connection and delivers ('recovery', 2).

Lingua principale
Python
Stelle
2.9k
Fork
708
Merge medio
1g 5h
PR unite (30g)
4

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di binance/binance-connector-python

Tutte le issue di binance/binance-connector-python

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.