Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto Apto para principiantes
#576 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
82/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
api, backend

Línea de trabajo

Lee common/src/binance_common/websocket.py, especialmente _discard_connection() y _forget_connection_streams(), y luego ejecuta la reproducción descrita en el issue contra un servidor WebSocket local. Comprueba que, después de que se pierda la conexión, al volver a suscribirse con el mismo cliente se envíe SUBSCRIBE y se entregue la actualización de recuperación. El issue sugiere olvidar los streams de la conexión muerta al final de _discard_connection().

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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).

Lenguaje dominante
Python
Estrellas
2.9k
Forks
708
Merge medio
1 d 5 h
PR fusionados (30 d)
4

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de binance/binance-connector-python

Todos los issues de binance/binance-connector-python

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.