separate ping from pong

this separates concerns and allows the pong_timeout to strictly handle timeouts for pongs.

patch bump
This commit is contained in:
onyx-and-iris 2026-03-03 19:47:15 +00:00
parent 2dd52a7258
commit 3d01321be3
3 changed files with 23 additions and 37 deletions

View File

@ -1,6 +1,6 @@
[project]
name = "vban-cmd"
version = "2.9.5"
version = "2.9.6"
description = "Python interface for the VBAN RT Packet Service (Sendtext)"
authors = [{ name = "Onyx and Iris", email = "code@onyxandiris.online" }]
license = { text = "MIT" }

View File

@ -21,8 +21,8 @@ def ratelimit(func):
return wrapper
def ping_timeout(func):
"""ping_timeout decorator for {VbanCmd}._ping, to handle timeout logic and socket management."""
def pong_timeout(func):
"""pong_timeout decorator for {VbanCmd}._handle_pong, to handle timeout logic and socket management."""
def wrapper(self, timeout: float = None):
if timeout is None:
@ -32,25 +32,14 @@ def ping_timeout(func):
self.sock.settimeout(0.5)
try:
func(self)
start_time = time.time()
response_count = 0
while time.time() - start_time < timeout:
try:
data, addr = self.sock.recvfrom(2048)
response_count += 1
self.logger.debug(
f'Received packet #{response_count} from {addr}: {len(data)} bytes'
)
self.logger.debug(
f'Response header: {data[: min(32, len(data))].hex()}'
)
result = func(self, data, addr)
if result is True:
if func(self):
return
except socket.timeout:

View File

@ -17,7 +17,7 @@ from .packet.headers import (
)
from .packet.ping0 import VbanPing0Payload, VbanServerType
from .subject import Subject
from .util import bump_framecounter, deep_merge, ping_timeout, ratelimit
from .util import bump_framecounter, deep_merge, pong_timeout, ratelimit
from .worker import Producer, Subscriber, Updater
logger = logging.getLogger(__name__)
@ -59,7 +59,6 @@ class VbanCmd(abc.ABC):
@abc.abstractmethod
def __str__(self):
"""Ensure subclasses override str magic method"""
pass
def _conn_from_toml(self) -> dict:
try:
@ -97,6 +96,7 @@ class VbanCmd(abc.ABC):
If the server is detected as Matrix, RT listeners will be disabled for compatibility.
"""
self._ping()
self._handle_pong()
if not self.disable_rt_listeners:
self.event.info()
@ -138,20 +138,12 @@ class VbanCmd(abc.ABC):
self._framecounter = bump_framecounter(self._framecounter)
return current
@ping_timeout
def _ping(self, data=None, addr=None) -> bool:
"""Handles the PING/PONG handshake with the VBAN server, including timeout logic and server type detection.
If data and addr are None, it sends a PING packet. If a PONG response is received, it returns True.
If a non-PONG packet is received, it logs the packet details and continues waiting until timeout"""
if data is None and addr is None:
def _ping(self):
"""Initiates the PING/PONG handshake with the VBAN server."""
ping_packet = VbanPing0Payload.create_packet(self._get_next_framecounter())
try:
self.sock.sendto(
ping_packet, (socket.gethostbyname(self.host), self.port)
)
self.sock.sendto(ping_packet, (socket.gethostbyname(self.host), self.port))
self.logger.debug(f'PING sent to {self.host}:{self.port}')
except socket.gaierror as e:
@ -161,7 +153,12 @@ class VbanCmd(abc.ABC):
except Exception as e:
raise VBANCMDConnectionError(f'PING failed: {e}') from e
return False
@pong_timeout
def _handle_pong(self) -> bool:
"""Handles incoming packets during the PING/PONG handshake, looking for a valid PONG response to confirm connectivity and detect server type.
Returns True if a valid PONG is received, False otherwise."""
data, addr = self.sock.recvfrom(2048)
if VbanPongHeader.is_pong_response(data):
self.logger.debug(f'PONG received from {addr}, connectivity confirmed')