From b8294cfb2f1b174dbe50ab47ad1563ad266b542c Mon Sep 17 00:00:00 2001 From: Florent Date: Thu, 12 Feb 2026 21:19:11 -0400 Subject: [PATCH] change frame reader for TCP --- pyproject.toml | 2 +- src/meshcore/tcp_cx.py | 67 +++++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5820aba..638b8ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "meshcore" -version = "2.2.8" +version = "2.2.9" authors = [ { name="Florent de Lamotte", email="florent@frizoncorrea.fr" }, { name="Alex Wolden", email="awolden@gmail.com" }, diff --git a/src/meshcore/tcp_cx.py b/src/meshcore/tcp_cx.py index 81e7cb9..de425b9 100644 --- a/src/meshcore/tcp_cx.py +++ b/src/meshcore/tcp_cx.py @@ -18,12 +18,12 @@ class TCPConnection: self.port = port self.transport = None self.frame_started = False - self.frame_size = 0 - self.header = b"" - self.inframe = b"" self._disconnect_callback = None self._send_count = 0 self._receive_count = 0 + self.frame_expected_size = 0 + self.header = b"" + self.inframe = b"" class MCClientProtocol(asyncio.Protocol): def __init__(self, cx): @@ -68,28 +68,49 @@ class TCPConnection: self.reader = reader def handle_rx(self, data: bytearray): - headerlen = len(self.header) - framelen = len(self.inframe) - if not self.frame_started: # wait start of frame - if len(data) >= 3 - headerlen: - self.header = self.header + data[: 3 - headerlen] - self.frame_started = True - self.frame_size = int.from_bytes(self.header[1:], byteorder="little") - self.handle_rx(data[3 - headerlen :]) - else: - self.header = self.header + data - else: - if framelen + len(data) < self.frame_size: - self.inframe = self.inframe + data - else: - self.inframe = self.inframe + data[: self.frame_size - framelen] - if self.reader is not None: - asyncio.create_task(self.reader.handle_rx(self.inframe)) - self.frame_started = False + if len(self.header) == 0: # did not find start of frame yet + # search start of frame (0x3e) in data + idx = data.find(b"\x3e") + if idx < 0: # no start of frame + return + self.header = data[0:1] + data = data[1:] + + if len(self.header) < 3: # header not complete yet + while len(self.header) < 3 and len(data) > 0: + self.header = self.header + data[0:1] + data = data[1:] + if len(self.header) < 3: # still not complete + return + + # get size and check + self.frame_expected_size = int.from_bytes(self.header[1:], "little", signed=False) + if self.frame_expected_size > 300 : # invalid size + # reset inframe self.header = b"" self.inframe = b"" - if framelen + len(data) > self.frame_size: - self.handle_rx(data[self.frame_size - framelen :]) + self.frame_expected_size = 0 + if len(data) > 0: # rerun handle_rx on remaining data + self.handle_rx(data) + return + + upbound = self.frame_expected_size - len(self.inframe) + if len(data) < upbound : + self.inframe = self.inframe + data + # frame not complete, wait for next rx + return + + self.inframe = self.inframe + data[0:upbound] + data = data[upbound:] + if self.reader is not None: + # feed meshcore reader + asyncio.create_task(self.reader.handle_rx(self.inframe)) + # reset inframe + self.inframe = b"" + self.header = b"" + self.frame_expected_size = 0 + if len(data) > 0: # rerun handle_rx on remaining data + self.handle_rx(data) async def send(self, data): if not self.transport: