From 0f82066ae7e483d74d8a4940b35678c036a9c955 Mon Sep 17 00:00:00 2001 From: bai <1145000687@qq.com> Date: Mon, 18 Dec 2023 12:20:32 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/socketd/socketd/core/async_utils.py | 84 ------------------- .../impl/AIOWebSocketClientImpl.py | 5 +- 2 files changed, 3 insertions(+), 86 deletions(-) delete mode 100644 python/socketd/socketd/core/async_utils.py diff --git a/python/socketd/socketd/core/async_utils.py b/python/socketd/socketd/core/async_utils.py deleted file mode 100644 index 024b56b3..00000000 --- a/python/socketd/socketd/core/async_utils.py +++ /dev/null @@ -1,84 +0,0 @@ -import asyncio -from concurrent.futures import ThreadPoolExecutor, as_completed, wait, ALL_COMPLETED -from common.globalconfig import global_config - -as_completed = as_completed -wait = wait -ALL_COMPLETED = ALL_COMPLETED - -ThreadPool = ThreadPoolExecutor() -# 生产最大线程10个 -if global_config.active == "prod": - ThreadPool._max_workers = 10 - - -class AsyncUtils: - - @staticmethod - def run(func): - return asyncio.run(func) - - @staticmethod - def run_coroutine(coro): - """运行一个协程并返回结果""" - loop = asyncio.get_event_loop() - task = loop.create_task(coro) - return task - - @staticmethod - async def async_core(core): - return core() - - @staticmethod - def run_futures(coro): - """运行一个协程并返回结果""" - loop = asyncio.get_event_loop() - task = loop.create_task(coro) - loop.run_until_complete(asyncio.wait(task)) - loop.close() - return task.result() - - @staticmethod - async def to_thread(func, *args, **kwargs): - """将一个阻塞型函数转换成协程任务,使用线程池异步执行""" - loop = asyncio.get_running_loop() - return await loop.run_in_executor(ThreadPool, lambda: func(*args, **kwargs)) - - @staticmethod - async def gather_concurrent(coros, limit=10): - """并发执行多个协程任务,并限制同时执行的数量""" - - async def worker(semaphore, coro): - async with semaphore: - return await coro - if limit < 0: - return await asyncio.gather(*coros) - semaphore = asyncio.Semaphore(limit) - tasks = [worker(semaphore, coro) for coro in coros] - return await asyncio.gather(*tasks) - - @staticmethod - async def run_with_timeout(coro, timeout=None): - """运行一个协程任务,并设置超时时间""" - task = asyncio.ensure_future(coro) - done, pending = await asyncio.wait([task], timeout=timeout) - if task in done: - return task.result() - else: - raise asyncio.TimeoutError() - - @staticmethod - async def repeat_task(coro, interval): - """重复运行一个协程任务,每隔一定时间间隔执行一次""" - while True: - await coro - await asyncio.sleep(interval) - - @staticmethod - async def sleep_until(timestamp): - """将当前协程挂起一段时间,直到指定的时间点""" - now = asyncio.get_event_loop().time() - if timestamp > now: - await asyncio.sleep(timestamp - now) - else: - raise ValueError("timestamp must be in the future") diff --git a/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py b/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py index 2763380c..40158d70 100644 --- a/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py +++ b/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py @@ -52,6 +52,7 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): try: await self.on_message() except Exception as e: + log.error(e) break def thread__handler(_loop): @@ -72,7 +73,7 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): await self.channel.send_connect(self.client.get_config().get_url()) await self.on_message() except Exception as e: - log.warning(str(e), exc_info=True) + log.error(str(e), exc_info=True) raise e async def on_message(self): @@ -93,7 +94,7 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): # 超时自动推出 log.debug(c) except Exception as e: - log.warning(str(e), exc_info=True) + log.error(str(e), exc_info=True) raise e def on_close(self): -- Gitee From 94a499f5e2951551bbc6e3af9b3e95f62cc42533 Mon Sep 17 00:00:00 2001 From: bai <1145000687@qq.com> Date: Thu, 21 Dec 2023 16:39:33 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/benchmark/cases/ClientTest.java | 36 ++++++++++ .../test/java/benchmark/cases/ServerTest.java | 46 +++++++++++++ .../socketd/transport/core/AsyncUtil.py | 18 +++++ .../transport/core/HeartbeatHandlerDefault.py | 14 ++++ .../socketd/socketd_websocket/WsAioServer.py | 10 +-- .../impl/AIOWebSocketServerImpl.py | 2 +- python/socketd/test/FurtureTest.py | 14 ++++ python/socketd/test/TestCase00.py | 32 +++++++++ python/socketd/test/TestCase01.py | 38 +++++++++++ python/socketd/test/base_test/__init__.py | 0 .../test/cases/TestCase01_client_send.py | 65 ++++++++++++++++++ .../test/cases/TestCase02_auto_reconnect.py | 66 +++++++++++++++++++ python/socketd/test/cases/__init__.py | 0 python/socketd/test/modelu/BaseTestCase.py | 16 +++++ .../socketd/test/modelu/SimpleListenerTest.py | 23 ++++++- 15 files changed, 373 insertions(+), 7 deletions(-) create mode 100644 java/socketd-transport-test/src/test/java/benchmark/cases/ClientTest.java create mode 100644 java/socketd-transport-test/src/test/java/benchmark/cases/ServerTest.java create mode 100644 python/socketd/socketd/transport/core/AsyncUtil.py create mode 100644 python/socketd/socketd/transport/core/HeartbeatHandlerDefault.py create mode 100644 python/socketd/test/FurtureTest.py create mode 100644 python/socketd/test/TestCase00.py create mode 100644 python/socketd/test/TestCase01.py create mode 100644 python/socketd/test/base_test/__init__.py create mode 100644 python/socketd/test/cases/TestCase01_client_send.py create mode 100644 python/socketd/test/cases/TestCase02_auto_reconnect.py create mode 100644 python/socketd/test/cases/__init__.py create mode 100644 python/socketd/test/modelu/BaseTestCase.py diff --git a/java/socketd-transport-test/src/test/java/benchmark/cases/ClientTest.java b/java/socketd-transport-test/src/test/java/benchmark/cases/ClientTest.java new file mode 100644 index 00000000..64d83dc0 --- /dev/null +++ b/java/socketd-transport-test/src/test/java/benchmark/cases/ClientTest.java @@ -0,0 +1,36 @@ +package benchmark.cases; + +import org.noear.socketd.SocketD; +import org.noear.socketd.transport.client.ClientSession; +import org.noear.socketd.transport.core.Entity; +import org.noear.socketd.transport.core.Frame; +import org.noear.socketd.transport.core.Message; +import org.noear.socketd.transport.core.Session; +import org.noear.socketd.transport.core.entity.StringEntity; +import org.noear.socketd.transport.core.identifier.TimeidGenerator; + +import java.io.IOException; + +public class ClientTest { + + ClientSession clientSession; + + + public void client() throws IOException { + //client + String serverUrl = "ws" + "://127.0.0.1:" + 7779 + "/path?u=a&p=2"; + clientSession = SocketD.createClient(serverUrl) + .config(config -> config.idGenerator(new TimeidGenerator()) + .requestTimeout(5000)) + .open(); +// clientSession.send("test", new StringEntity("test")); + Entity entity = clientSession.sendAndRequest("test", new StringEntity("test")); + System.out.println(entity); + clientSession.close(); + } + + public static void main(String[] args) throws IOException { + ClientTest clientTest = new ClientTest(); + clientTest.client(); + } +} diff --git a/java/socketd-transport-test/src/test/java/benchmark/cases/ServerTest.java b/java/socketd-transport-test/src/test/java/benchmark/cases/ServerTest.java new file mode 100644 index 00000000..5d66d5b5 --- /dev/null +++ b/java/socketd-transport-test/src/test/java/benchmark/cases/ServerTest.java @@ -0,0 +1,46 @@ +package benchmark.cases; + +import org.noear.socketd.SocketD; +import org.noear.socketd.transport.core.Message; +import org.noear.socketd.transport.core.Session; +import org.noear.socketd.transport.core.entity.StringEntity; +import org.noear.socketd.transport.core.identifier.TimeidGenerator; +import org.noear.socketd.transport.core.listener.SimpleListener; +import org.noear.socketd.transport.server.Server; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +public class ServerTest { + + private CountDownLatch sendLatch; + + private Server server; + private Session clientSession; + + + public void server() throws IOException { + //server + server = SocketD.createServer("ws") + .config(c -> c.port(7779).idGenerator(new TimeidGenerator())) + .listen(new SimpleListener() { + @Override + public void onMessage(Session session, Message message) throws IOException { + if (message.isRequest()) { + session.replyEnd(message, new StringEntity("test")); + } else if (message.isSubscribe()) { + session.replyEnd(message, new StringEntity("test")); + } else { + sendLatch.countDown(); + } + System.out.println(message.toString()); + } + }) + .start(); + } + + public static void main(String[] args) throws IOException { + new ServerTest().server(); + } + +} diff --git a/python/socketd/socketd/transport/core/AsyncUtil.py b/python/socketd/socketd/transport/core/AsyncUtil.py new file mode 100644 index 00000000..bb95e5fa --- /dev/null +++ b/python/socketd/socketd/transport/core/AsyncUtil.py @@ -0,0 +1,18 @@ +import asyncio +from typing import Coroutine, Any + + +class AsyncUtil: + + @staticmethod + def thread_handler(_loop, fn: asyncio.Task): + """ + 静态方法 thread_handler 用于在指定的事件循环中运行一个协程函数。 + + 参数: + _loop (EventLoop): 事件循环对象,用于控制协程的执行。 + fn (Coroutine): 需要运行的协程函数。 + """ + asyncio.set_event_loop(_loop) + _loop.run_until_complete(fn) + diff --git a/python/socketd/socketd/transport/core/HeartbeatHandlerDefault.py b/python/socketd/socketd/transport/core/HeartbeatHandlerDefault.py new file mode 100644 index 00000000..eee7a824 --- /dev/null +++ b/python/socketd/socketd/transport/core/HeartbeatHandlerDefault.py @@ -0,0 +1,14 @@ +from abc import ABC + +from socketd.core.Session import Session + + +class HeartbeatHandler(ABC): + + def heartbeat(self, session: Session): ... + + +class HeartbeatHandlerDefault(HeartbeatHandler): + + async def heartbeat(self, session: Session): + await session.send_ping() diff --git a/python/socketd/socketd_websocket/WsAioServer.py b/python/socketd/socketd_websocket/WsAioServer.py index a04ec8ca..b03ecc90 100644 --- a/python/socketd/socketd_websocket/WsAioServer.py +++ b/python/socketd/socketd_websocket/WsAioServer.py @@ -17,13 +17,13 @@ class WsAioServer(ServerBase): super().__init__(config, WsAioChannelAssistant(config)) self.__loop = asyncio.get_event_loop() self.server: Serve = None - self._stop = asyncio.Future() # set this future to exit the server + self.__is_started = False async def start(self) -> 'WebSocketServer': - if self.isStarted: + if self.__is_started: raise Exception("Server started") else: - self.isStarted = True + self.__is_started = True if self._config.get_host() is not None: _server = AIOServe(ws_handler=None, host="0.0.0.0", port=self._config.get_port(), @@ -50,5 +50,5 @@ class WsAioServer(ServerBase): async def stop(self): logger.info("WsAioServer stop...") - await self.server.ws_server.close() - await self._stop + self.server.ws_server.close() + self.__is_started = False diff --git a/python/socketd/socketd_websocket/impl/AIOWebSocketServerImpl.py b/python/socketd/socketd_websocket/impl/AIOWebSocketServerImpl.py index 1e3dd1df..8d1923ce 100644 --- a/python/socketd/socketd_websocket/impl/AIOWebSocketServerImpl.py +++ b/python/socketd/socketd_websocket/impl/AIOWebSocketServerImpl.py @@ -31,7 +31,7 @@ class AIOWebSocketServerImpl(WebSocketServerProtocol, IWebSocketServer): def connection_open(self) -> None: """握手完成回调""" super().connection_open() - log.debug("AIOWebSocketServerImpl 打开握手完成回调") + log.debug("AIOWebSocketServerImpl connection_open") self.on_open(self) def handshake_handler(self): diff --git a/python/socketd/test/FurtureTest.py b/python/socketd/test/FurtureTest.py new file mode 100644 index 00000000..855f17dc --- /dev/null +++ b/python/socketd/test/FurtureTest.py @@ -0,0 +1,14 @@ +import asyncio + +import unittest + + +class FutureTest(unittest.TestCase): + + def test_wait(self): + async def _wait(): + top = asyncio.Future() + top.set_result(0) + await top + + asyncio.run(_wait()) diff --git a/python/socketd/test/TestCase00.py b/python/socketd/test/TestCase00.py new file mode 100644 index 00000000..403d8bbd --- /dev/null +++ b/python/socketd/test/TestCase00.py @@ -0,0 +1,32 @@ +import asyncio +import unittest +import sys +from loguru import logger + +from test.modelu.BaseTest import BaseTest + + +class TestCase00(unittest.TestCase): + + count = 100000 + timeout = 30 + + def __init__(self, *args, **kwargs): + logger.remove() + logger.add(sys.stderr, level="INFO") + super().__init__(*args, **kwargs) + + def test_send(self): + test = BaseTest() + loop = asyncio.new_event_loop() + try: + loop.run_until_complete(test.start()) + loop.run_until_complete(test.send(TestCase00.count)) + # loop.run_until_complete(test.send_and_request(TestCase00.count)) + # loop.run_until_complete(test.send_and_subscribe(TestCase00.count)) + except Exception as e: + pass + finally: + loop.run_until_complete(test.close()) + + diff --git a/python/socketd/test/TestCase01.py b/python/socketd/test/TestCase01.py new file mode 100644 index 00000000..4b586f57 --- /dev/null +++ b/python/socketd/test/TestCase01.py @@ -0,0 +1,38 @@ +import asyncio +import unittest +import sys +from loguru import logger + +from test.cases.TestCase01_client_send import TestCase01_client_send +from test.cases.TestCase02_auto_reconnect import TestCase02_auto_reconnect +from test.modelu.BaseTest import BaseTest + + +class TestCase01(unittest.TestCase): + + schemas = ["ws"] + + def __init__(self, *args, **kwargs): + logger.remove() + logger.add(sys.stderr, level="DEBUG") + super().__init__(*args, **kwargs) + + def test_Case01_client_send(self): + for i in range(len(TestCase01.schemas)): + t = TestCase01_client_send(TestCase01.schemas[i], 9000 + i) + try: + t.start() + t.stop() + except Exception as e: + t.on_error() + raise e + + def test_Case02_auto_reconnect(self): + for i in range(len(TestCase01.schemas)): + t = TestCase02_auto_reconnect(TestCase01.schemas[i], 9000 + i) + try: + t.start() + t.stop() + except Exception as e: + t.on_error() + raise e diff --git a/python/socketd/test/base_test/__init__.py b/python/socketd/test/base_test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/socketd/test/cases/TestCase01_client_send.py b/python/socketd/test/cases/TestCase01_client_send.py new file mode 100644 index 00000000..58eb1cc0 --- /dev/null +++ b/python/socketd/test/cases/TestCase01_client_send.py @@ -0,0 +1,65 @@ +import asyncio + +from test.modelu.BaseTestCase import BaseTestCase + +import time +from websockets.legacy.server import WebSocketServer + +from socketd.core.Session import Session +from socketd.core.SocketD import SocketD +from socketd.core.config.ServerConfig import ServerConfig +from socketd.core.module.StringEntity import StringEntity +from socketd.transport.server.Server import Server +from test.modelu.SimpleListenerTest import SimpleListenerTest, config_handler, send_and_subscribe_test +from loguru import logger + + +class TestCase01_client_send(BaseTestCase): + + def __init__(self, schema, port): + super().__init__(schema, port) + self.server: Server = None + self.server_session: WebSocketServer = None + self.client_session: Session = None + self.loop = asyncio.get_event_loop() + + async def _start(self): + self.server: Server = SocketD.create_server(ServerConfig(self.schema).set_port(self.port)) + self.server_session: WebSocketServer = await self.server.config(config_handler).listen( + SimpleListenerTest()).start() + + serverUrl = self.schema + "://127.0.0.1:" + str(self.port) + "/path?u=a&p=2" + self.client_session: Session = await SocketD.create_client(serverUrl) \ + .config(config_handler).open() + await self.client_session.send_and_request("demo", StringEntity("test"), 100) + + start_time = time.monotonic() + for _ in range(3): + await self.client_session.send("demo", StringEntity("test")) + + await self.client_session.send_and_subscribe("demo", StringEntity("test"), send_and_subscribe_test, 100) + end_time = time.monotonic() + logger.info(f"Coroutine send took {(end_time - start_time) * 1000.0} monotonic to complete.") + await asyncio.sleep(3) + + def start(self): + super().start() + + self.loop.run_until_complete(self._start()) + + async def _stop(self): + if self.client_session: + await self.client_session.close() + + if self.server_session: + self.server_session.close() + if self.server: + await self.server.stop() + + def stop(self): + super().stop() + + self.loop.run_until_complete(self._stop()) + + def on_error(self): + super().on_error() diff --git a/python/socketd/test/cases/TestCase02_auto_reconnect.py b/python/socketd/test/cases/TestCase02_auto_reconnect.py new file mode 100644 index 00000000..43518734 --- /dev/null +++ b/python/socketd/test/cases/TestCase02_auto_reconnect.py @@ -0,0 +1,66 @@ +import asyncio + +from test.modelu.BaseTestCase import BaseTestCase + +from websockets.legacy.server import WebSocketServer +from loguru import logger + +from socketd.core.Session import Session +from socketd.core.SocketD import SocketD +from socketd.core.config.ServerConfig import ServerConfig +from socketd.core.module.StringEntity import StringEntity +from socketd.transport.server.Server import Server +from test.modelu.SimpleListenerTest import SimpleListenerTest, config_handler, send_and_subscribe_test + + +class TestCase02_auto_reconnect(BaseTestCase): + + def __init__(self, schema, port): + super().__init__(schema, port) + self.server: Server + self.server_session: WebSocketServer + self.client_session: Session + self.loop = asyncio.get_event_loop() + + async def _start(self): + self.server: Server = SocketD.create_server(ServerConfig(self.schema).set_port(self.port)) + _simple = SimpleListenerTest() + _server = self.server.config(config_handler).listen(_simple) + self.server_session: WebSocketServer = await _server.start() + + serverUrl = self.schema + "://127.0.0.1:" + str(self.port) + "/path?u=a&p=2" + self.client_session: Session = await SocketD.create_client(serverUrl) \ + .config(config_handler).open() + await self.client_session.send_and_request("demo", StringEntity("test"), 100) + + await self.server.stop() + del self.server_session + await asyncio.sleep(10) + self.server_session = await _server.start() + + for _ in range(3): + await self.client_session.send("demo", StringEntity("test")) + + await self.client_session.send_and_subscribe("demo", StringEntity("test"), send_and_subscribe_test, 100) + await asyncio.sleep(1) + logger.info(f"counter {_simple.server_counter.get()} ") + + def start(self): + super().start() + self.loop.run_until_complete(self._start()) + + async def _stop(self): + if self.client_session: + await self.client_session.close() + + if self.server_session: + self.server_session.close() + if self.server: + await self.server.stop() + + def stop(self): + super().stop() + self.loop.run_until_complete(self._stop()) + + def on_error(self): + super().on_error() diff --git a/python/socketd/test/cases/__init__.py b/python/socketd/test/cases/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python/socketd/test/modelu/BaseTestCase.py b/python/socketd/test/modelu/BaseTestCase.py new file mode 100644 index 00000000..8a4d3ee1 --- /dev/null +++ b/python/socketd/test/modelu/BaseTestCase.py @@ -0,0 +1,16 @@ +from loguru import logger + +class BaseTestCase: + + def __init__(self, schema, port): + self.schema = schema + self.port = port + + def start(self): + logger.info (f"--------START {self.schema}------") + + def stop(self): + logger.info(f"--------STOP {self.schema}------") + + def on_error(self): + logger.info(f"--------ERROR {self.schema}------") diff --git a/python/socketd/test/modelu/SimpleListenerTest.py b/python/socketd/test/modelu/SimpleListenerTest.py index 33611004..3ffbb4be 100644 --- a/python/socketd/test/modelu/SimpleListenerTest.py +++ b/python/socketd/test/modelu/SimpleListenerTest.py @@ -2,20 +2,36 @@ import uuid from abc import ABC from socketd.core.Listener import Listener +from socketd.core.config.ClientConfig import ClientConfig +from socketd.core.config.ServerConfig import ServerConfig +from socketd.core.module.Entity import Entity from socketd.core.module.Message import Message from socketd.core.module.StringEntity import StringEntity +from loguru import logger + +from socketd.core.sync_api.AtomicRefer import AtomicRefer + class SimpleListenerTest(Listener, ABC): + def __init__(self): + self.server_counter = AtomicRefer(0) + def on_open(self, session): pass async def on_message(self, session, message: Message): + with self.server_counter: + self.server_counter.set(self.server_counter.get() + 1) if message.is_request(): + await session.reply(message, StringEntity("reply")) await session.reply_end(message, StringEntity("ok test")) + await session.reply(message, StringEntity("reply")) elif message.is_subscribe(): + await session.reply(message, StringEntity("reply")) await session.reply_end(message, StringEntity("ok test")) + await session.reply(message, StringEntity("reply")) def on_close(self, session): pass @@ -24,5 +40,10 @@ class SimpleListenerTest(Listener, ABC): pass -def idGenerator(config): +def config_handler(config: ServerConfig | ClientConfig) -> ServerConfig | ClientConfig: + config.set_is_thread(False) return config.id_generator(uuid.uuid4) + + +def send_and_subscribe_test(e: Entity): + logger.info(e) -- Gitee From 8a8fdaf85203decbf6f03ace9813d022b7e87f3d Mon Sep 17 00:00:00 2001 From: bai <1145000687@qq.com> Date: Thu, 21 Dec 2023 16:39:41 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/features/CaseTest.java | 6 ++- .../transport/client/ClientChannel.java | 2 +- python/socketd/reqeusts.txt | 5 +-- python/socketd/socketd/core/ChannelDefault.py | 7 ++-- python/socketd/socketd/core/Session.py | 24 +++++++++-- python/socketd/socketd/core/SessionDefault.py | 14 ++++--- .../socketd/core/config/ClientConfig.py | 9 +--- .../socketd/socketd/core/config/ConfigBase.py | 7 ++++ .../socketd/transport/client/ClientChannel.py | 41 +++++++++++++++---- .../transport/core/CompletableFuture.py | 1 - .../socketd/transport/core/StreamBase.py | 1 - .../socketd/transport/server/Server.py | 6 +-- .../socketd/socketd_websocket/WsAioFactoy.py | 2 +- .../impl/AIOWebSocketClientImpl.py | 23 +++++++---- python/socketd/test/00_TestCase.py | 32 --------------- .../test/{ => base_test}/01_applictionTest.py | 2 +- .../test/{ => base_test}/02_ClientTest.py | 0 .../test/{ => base_test}/02_ServerTest.py | 0 .../test/{ => base_test}/04_websockets.py | 1 - 19 files changed, 98 insertions(+), 85 deletions(-) delete mode 100644 python/socketd/test/00_TestCase.py rename python/socketd/test/{ => base_test}/01_applictionTest.py (98%) rename python/socketd/test/{ => base_test}/02_ClientTest.py (100%) rename python/socketd/test/{ => base_test}/02_ServerTest.py (100%) rename python/socketd/test/{ => base_test}/04_websockets.py (99%) diff --git a/java/socketd-transport-test/src/test/java/features/CaseTest.java b/java/socketd-transport-test/src/test/java/features/CaseTest.java index f52a044f..8bbcf7b2 100644 --- a/java/socketd-transport-test/src/test/java/features/CaseTest.java +++ b/java/socketd-transport-test/src/test/java/features/CaseTest.java @@ -9,7 +9,9 @@ import org.junit.jupiter.api.Test; */ public class CaseTest { static final String[] schemas = new String[]{ - "sd:tcp-java", "sd:tcp-netty", "sd:tcp-smartsocket", + "sd:tcp-java", +// "sd:tcp-netty", +// "sd:tcp-smartsocket", "sd:ws-java", "sd:udp-java"}; @@ -17,7 +19,7 @@ public class CaseTest { public void TestCase01_client_send() throws Exception { for (int i = 0; i < schemas.length; i++) { String s1 = schemas[i]; - BaseTestCase testCase = new TestCase01_client_send(s1, 1000 + i); + BaseTestCase testCase = new TestCase01_client_send(s1, 9000 + i); try { testCase.start(); testCase.stop(); diff --git a/java/socketd/src/main/java/org/noear/socketd/transport/client/ClientChannel.java b/java/socketd/src/main/java/org/noear/socketd/transport/client/ClientChannel.java index c87c753e..546eadf9 100644 --- a/java/socketd/src/main/java/org/noear/socketd/transport/client/ClientChannel.java +++ b/java/socketd/src/main/java/org/noear/socketd/transport/client/ClientChannel.java @@ -227,7 +227,7 @@ public class ClientChannel extends ChannelBase implements Channel { * @return 是否为新链接 */ private boolean prepareCheck() throws IOException { - if (real == null || real.isValid() == false) { + if (real == null || !real.isValid()) { real = connector.connect(); return true; diff --git a/python/socketd/reqeusts.txt b/python/socketd/reqeusts.txt index f12ae6ca..ee89d343 100644 --- a/python/socketd/reqeusts.txt +++ b/python/socketd/reqeusts.txt @@ -1,5 +1,2 @@ -colorama==0.4.6 -connect==0.2 loguru==0.7.2 -nest-asyncio==1.5.8 -websockets==12.0 +websockets==12.0 \ No newline at end of file diff --git a/python/socketd/socketd/core/ChannelDefault.py b/python/socketd/socketd/core/ChannelDefault.py index ee8bb565..8d127085 100644 --- a/python/socketd/socketd/core/ChannelDefault.py +++ b/python/socketd/socketd/core/ChannelDefault.py @@ -79,10 +79,11 @@ class ChannelDefault(ChannelBase): if acceptor.is_single() or frame.get_flag() == Flag.ReplyEnd: self.acceptorMap.pop(frame.get_message().get_sid()) await asyncio.get_event_loop().run_in_executor(self.get_config().get_executor(), - lambda _m: acceptor.on_accept(_m, onError), frame.get_message()) + lambda _m: acceptor.on_accept(_m, onError), + frame.get_message()) else: - logger.debug(f"{self.get_config().getRoleName()} stream not found, sid={frame.get_message().get_sid()}, sessionId={self.get_session().get_session_id()}") - + logger.debug( + f"{self.get_config().get_role_name()} stream not found, sid={frame.get_message().get_sid()}, sessionId={self.get_session().get_session_id()}") def get_session(self) -> Session: if self.session is None: diff --git a/python/socketd/socketd/core/Session.py b/python/socketd/socketd/core/Session.py index 6615f0f6..45f46134 100644 --- a/python/socketd/socketd/core/Session.py +++ b/python/socketd/socketd/core/Session.py @@ -1,5 +1,5 @@ import abc -from typing import Union, Dict, Any, Callable, Awaitable +from typing import Union, Dict, Any, Callable, Awaitable, AsyncGenerator, Coroutine from socket import gethostbyaddr from socketd.core.Handshake import Handshake from socketd.core.module.Message import Message @@ -44,7 +44,7 @@ class Session(abc.ABC): ... @abc.abstractmethod - def send_ping(self) -> None: + def send_ping(self) -> Callable | Coroutine: ... @abc.abstractmethod @@ -55,8 +55,23 @@ class Session(abc.ABC): async def send_and_request(self, topic: str, content: Entity, timeout: int) -> Entity: ... - async def send_stream_and_request(self, event: str, content: Entity, consumer: Callable[[Entity], Awaitable[Any]], - timeout: int): ... + @abc.abstractmethod + async def send_stream_and_request(self, event: str, content: Entity, + consumer: Callable[[Entity], Awaitable[Any]] | Coroutine[Entity, Any, None], + timeout: int): + """ + 发送流和请求的抽象方法。 + + Args: + event (str): 事件名称。 + content (Entity): 内容实体。 + consumer (Callable[[Entity], Awaitable[Any]] | Coroutine[Entity, Awaitable[Any]]): 消费函数或协程,用来处理内容实体并返回异步结果。 + timeout (int): 超时时间。 + + Returns: + Awaitable[Any]: 消费函数或协程的异步结果。 + """ + ... @abc.abstractmethod async def send_and_subscribe(self, topic: str, content: Entity, consumer: Callable[[Entity], Any], @@ -71,5 +86,6 @@ class Session(abc.ABC): def reply_end(self, from_msg: Message, content: Entity) -> None: ... + @abc.abstractmethod def close(self): ... diff --git a/python/socketd/socketd/core/SessionDefault.py b/python/socketd/socketd/core/SessionDefault.py index 4080220c..c9a14225 100644 --- a/python/socketd/socketd/core/SessionDefault.py +++ b/python/socketd/socketd/core/SessionDefault.py @@ -1,7 +1,7 @@ import asyncio from abc import ABC -from typing import Callable, Awaitable, Any +from typing import Callable, Awaitable, Any, Coroutine from .SessionBase import SessionBase from .Channel import Channel @@ -34,8 +34,8 @@ class SessionDefault(SessionBase, ABC): def get_handshake(self) -> Handshake: return self.channel.get_handshake() - def send_ping(self): - self.channel.send_ping() + async def send_ping(self): + await self.channel.send_ping() async def send(self, topic: str, content: Entity): message = MessageDefault().set_sid(self.generate_id()).set_event(topic).set_entity(content) @@ -65,12 +65,16 @@ class SessionDefault(SessionBase, ABC): finally: self.channel.remove_acceptor(message.get_sid()) - async def send_stream_and_request(self, event: str, content: Entity, consumer: Callable[[Entity], Awaitable[Any]], + async def send_stream_and_request(self, event: str, content: Entity, + consumer: Callable[[Entity], Awaitable[Any]] | Coroutine[Entity, Any, None], timeout: int): message = MessageDefault().set_sid(self.generate_id()).set_event(event).set_entity(content) future: CompletableFuture[Entity] = CompletableFuture() try: - consumer(content) + if asyncio.iscoroutinefunction(consumer): + await consumer(content) + else: + self.channel.get_config().get_executor().submit(fn=consumer, args=(content,)) except Exception as e: self.channel.on_error(e) streamAcceptor = StreamRequest(message.get_sid(), timeout, future) diff --git a/python/socketd/socketd/core/config/ClientConfig.py b/python/socketd/socketd/core/config/ClientConfig.py index 5967e135..6c25b4b4 100644 --- a/python/socketd/socketd/core/config/ClientConfig.py +++ b/python/socketd/socketd/core/config/ClientConfig.py @@ -18,7 +18,6 @@ class ClientConfig(ConfigBase): self.__auto_reconnect = True self.__read_buffer_size = None self.__write_buffer_size = None - self.__is_thread = False def get_schema(self): return self.__schema @@ -70,12 +69,7 @@ class ClientConfig(ConfigBase): self.__auto_reconnect = __auto_reconnect return self - def is_thread(self, __is_thread: bool): - self.__is_thread = __is_thread - return self - def get_is_thread(self): - return self.__is_thread def __str__(self): return f"ClientConfig{{__schema='{self.__schema}', __url='{self.__url}', " \ @@ -85,5 +79,4 @@ class ClientConfig(ConfigBase): f"writeBufferSize={self.__write_buffer_size}, " \ f"autoReconnect={self.__auto_reconnect}, " \ f"maxRequests={self._max_requests}, " \ - f"maxUdpSize={self._max_udp_size}}}" \ - f"isThread={self.is_thread}}}" \ No newline at end of file + f"maxUdpSize={self._max_udp_size}}}" diff --git a/python/socketd/socketd/core/config/ConfigBase.py b/python/socketd/socketd/core/config/ConfigBase.py index 63cd82a9..86577279 100644 --- a/python/socketd/socketd/core/config/ConfigBase.py +++ b/python/socketd/socketd/core/config/ConfigBase.py @@ -27,6 +27,7 @@ class ConfigBase(Config): self._reply_timeout = 3000 self._max_requests = 10 self._max_udp_size = 2048 + self.__is_thread = False def client_mode(self): return self._client_mode @@ -128,3 +129,9 @@ class ConfigBase(Config): def set_stream_timeout(self, _stream_timeout): self._stream_timeout = _stream_timeout return self + + def set_is_thread(self, _is_thread): + self.__is_thread = _is_thread + + def get_is_thread(self): + return self.__is_thread diff --git a/python/socketd/socketd/transport/client/ClientChannel.py b/python/socketd/socketd/transport/client/ClientChannel.py index 042cb197..d1d4600a 100644 --- a/python/socketd/socketd/transport/client/ClientChannel.py +++ b/python/socketd/socketd/transport/client/ClientChannel.py @@ -1,4 +1,6 @@ +import asyncio from abc import ABC +from asyncio import Future from socketd.core.AssertsUtil import AssertsUtil from socketd.core.Channel import Channel @@ -6,6 +8,9 @@ from socketd.core.ChannelBase import ChannelBase from socketd.transport.client.ClientConnector import ClientConnector from loguru import logger +from socketd.transport.core.AsyncUtil import AsyncUtil +from socketd.transport.core.HeartbeatHandlerDefault import HeartbeatHandlerDefault + class ClientChannel(ChannelBase, ABC): def __init__(self, real: Channel, connector: ClientConnector): @@ -13,13 +18,17 @@ class ClientChannel(ChannelBase, ABC): self.real: Channel = real self.connector: ClientConnector = connector self.heartbeatHandler = connector.heartbeatHandler() + self._heartbeatScheduledFuture: Future | None = None + + if self.heartbeatHandler is None: + self.heartbeatHandler = HeartbeatHandlerDefault() - # if self.heartbeatHandler is None: - # self.heartbeatHandler = HeartbeatHandlerDefault() - connector.autoReconnect() - # if connector.autoReconnect() and self.heartbeatScheduledFuture is None: - # self.heartbeatScheduledFuture = threading.Timer(connector.heartbeatInterval(), self.heartbeatHandle) - # self.heartbeatScheduledFuture.start() + self._loop = asyncio.new_event_loop() + self.initHeartbeat() + + def __del__(self): + if self._loop: + self._loop.close() def remove_acceptor(self, sid): if self.real is not None: @@ -49,13 +58,27 @@ class ClientChannel(ChannelBase, ABC): else: return self.real.get_local_address() + def initHeartbeat(self): + if self._heartbeatScheduledFuture is not None: + self._heartbeatScheduledFuture.cancel() + + if self.connector.autoReconnect(): + async def _heartbeatScheduled(): + while True: + await asyncio.sleep(self.connector.heartbeatInterval()) + await self.heartbeat_handle() + self._heartbeatScheduledFuture = asyncio.create_task(_heartbeatScheduled()) + + self.get_config().get_executor().submit(lambda: + AsyncUtil.thread_handler(self._loop, self._heartbeatScheduledFuture)) + def heartbeat_handle(self): AssertsUtil.assert_closed(self.real) with self: try: self.prepare_send() - self.heartbeatHandler.heartbeat_handle() + self.heartbeatHandler.heartbeat(self.get_session()) except Exception as e: if self.connector.autoReconnect(): self.real.close() @@ -75,7 +98,7 @@ class ClientChannel(ChannelBase, ABC): raise e async def retrieve(self, frame, on_error): - self.real.retrieve(frame, on_error) + await self.real.retrieve(frame, on_error) def get_session(self): return self.real.get_session() @@ -84,6 +107,7 @@ class ClientChannel(ChannelBase, ABC): reason: str = "", ): try: await super().close(code, reason) + self._heartbeatScheduledFuture.cancel() if self.real is not None: await self.real.close() except Exception as e: @@ -101,4 +125,3 @@ class ClientChannel(ChannelBase, ABC): def on_error(self, error: Exception): pass - diff --git a/python/socketd/socketd/transport/core/CompletableFuture.py b/python/socketd/socketd/transport/core/CompletableFuture.py index dad3e595..322ac4b5 100644 --- a/python/socketd/socketd/transport/core/CompletableFuture.py +++ b/python/socketd/socketd/transport/core/CompletableFuture.py @@ -1,5 +1,4 @@ import asyncio -from asyncio.coroutines import iscoroutine from typing import Generic, TypeVar from loguru import logger diff --git a/python/socketd/socketd/transport/core/StreamBase.py b/python/socketd/socketd/transport/core/StreamBase.py index 6ffcc896..ac2501e7 100644 --- a/python/socketd/socketd/transport/core/StreamBase.py +++ b/python/socketd/socketd/transport/core/StreamBase.py @@ -12,7 +12,6 @@ class StreamBase(StreamInternal): self.__sid = sid self.__timeout = timeout self.__onError: Callable[[Exception], None] = None - self.insurance_future = Future() def on_error(self, error: Exception): if error: diff --git a/python/socketd/socketd/transport/server/Server.py b/python/socketd/socketd/transport/server/Server.py index 27c1b049..6d29bf7a 100644 --- a/python/socketd/socketd/transport/server/Server.py +++ b/python/socketd/socketd/transport/server/Server.py @@ -1,4 +1,4 @@ -from typing import Callable +from typing import Callable, Coroutine from asyncio import Future from websockets.sync.server import WebSocketServer @@ -15,9 +15,9 @@ class Server: def listen(self, listener: Listener) -> 'Server': ... - def start(self) -> WebSocketServer | Future: ... + def start(self) -> WebSocketServer | Coroutine: ... - def stop(self) -> Future: ... + def stop(self) -> Coroutine: ... def get_assistant(self): ... diff --git a/python/socketd/socketd_websocket/WsAioFactoy.py b/python/socketd/socketd_websocket/WsAioFactoy.py index 842ae5ea..60345565 100644 --- a/python/socketd/socketd_websocket/WsAioFactoy.py +++ b/python/socketd/socketd_websocket/WsAioFactoy.py @@ -10,7 +10,7 @@ from socketd_websocket.WsAioServer import WsAioServer class WsAioFactory(ClientFactory, ServerFactory): - def schema(self): + def schema(self) -> list[str]: return ["ws", "wss", "ws-python"] def create_server(self, serverConfig: ServerConfig) -> Server: diff --git a/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py b/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py index 40158d70..3fc25b36 100644 --- a/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py +++ b/python/socketd/socketd_websocket/impl/AIOWebSocketClientImpl.py @@ -12,6 +12,7 @@ from websockets import WebSocketClientProtocol, Origin, Subprotocol, HeadersLike from socketd.core.Costants import Flag from socketd.core.module.Frame import Frame from socketd.transport.client.Client import Client +from socketd.transport.core.AsyncUtil import AsyncUtil log = logger.opt() @@ -22,7 +23,7 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): self.status_state = Flag.Unknown self.client = client self.channel = ChannelDefault(self, client.get_config(), client.get_assistant()) - self.connect_read_thread: Thread = None + self.connect_read_thread: Thread | None = None def get_channel(self): return self.channel @@ -39,11 +40,17 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): return return_data def connection_open(self) -> None: - """打开握手完成回调""" + """ + 打开握手完成回调函数。 + :return: 无返回值 + """ super().connection_open() log.debug("AIOWebSocketClientImpl connection_open") async def _handler(): + """ + 异步处理函数,用于处理握手完成后的消息处理逻辑。 + """ while True: await asyncio.sleep(0) if self.closed or self.status_state == Flag.Close: @@ -54,17 +61,14 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): except Exception as e: log.error(e) break - - def thread__handler(_loop): - asyncio.set_event_loop(_loop) - _loop.run_until_complete(_handler()) - + # 如果配置中设置了使用线程,则创建一个新的事件循环,并启动一个线程来处理读取操作 if self.client.get_config().get_is_thread(): loop = asyncio.new_event_loop() if self.connect_read_thread is None: - self.connect_read_thread = Thread(target=thread__handler, args=(loop,)) + self.connect_read_thread = Thread(target=AsyncUtil.thread_handler, args=(loop,)) self.connect_read_thread.start() else: + # 否则,使用异步运行此协程,并在当前线程中运行。 asyncio.run_coroutine_threadsafe(_handler(), asyncio.get_event_loop()) async def on_open(self): @@ -79,6 +83,8 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): async def on_message(self): """处理消息""" try: + if self.status_state == Flag.Close: + return message = await self.recv() log.debug(message) frame: Frame = self.client.get_assistant().read(message) @@ -94,7 +100,6 @@ class AIOWebSocketClientImpl(WebSocketClientProtocol): # 超时自动推出 log.debug(c) except Exception as e: - log.error(str(e), exc_info=True) raise e def on_close(self): diff --git a/python/socketd/test/00_TestCase.py b/python/socketd/test/00_TestCase.py deleted file mode 100644 index 812b1789..00000000 --- a/python/socketd/test/00_TestCase.py +++ /dev/null @@ -1,32 +0,0 @@ -import asyncio -import unittest -import sys -from loguru import logger - -from test.modelu.BaseTest import BaseTest - - -class TestCase(unittest.TestCase): - - count = 10000 - timeout = 30 - - def __init__(self, *args, **kwargs): - logger.remove() - logger.add(sys.stderr, level="INFO") - super().__init__(*args, **kwargs) - - def test_send(self): - test = BaseTest() - loop = asyncio.get_event_loop() - try: - loop.run_until_complete(test.start()) - loop.run_until_complete(test.send(TestCase.count)) - loop.run_until_complete(test.send_and_request(TestCase.count)) - loop.run_until_complete(test.send_and_subscribe(TestCase.count)) - except Exception as e: - pass - finally: - test.close() - - diff --git a/python/socketd/test/01_applictionTest.py b/python/socketd/test/base_test/01_applictionTest.py similarity index 98% rename from python/socketd/test/01_applictionTest.py rename to python/socketd/test/base_test/01_applictionTest.py index c7ce4354..dbdd1b44 100644 --- a/python/socketd/test/01_applictionTest.py +++ b/python/socketd/test/base_test/01_applictionTest.py @@ -54,7 +54,7 @@ async def application_test(): server: Server = SocketD.create_server(ServerConfig("ws").set_port(9999)) server_session: WebSocketServer = await server.config(idGenerator).listen( SimpleListenerTest()).start() - + await asyncio.sleep(1) client_session: Session = await SocketD.create_client("ws://127.0.0.1:9999") \ .config(idGenerator).open() diff --git a/python/socketd/test/02_ClientTest.py b/python/socketd/test/base_test/02_ClientTest.py similarity index 100% rename from python/socketd/test/02_ClientTest.py rename to python/socketd/test/base_test/02_ClientTest.py diff --git a/python/socketd/test/02_ServerTest.py b/python/socketd/test/base_test/02_ServerTest.py similarity index 100% rename from python/socketd/test/02_ServerTest.py rename to python/socketd/test/base_test/02_ServerTest.py diff --git a/python/socketd/test/04_websockets.py b/python/socketd/test/base_test/04_websockets.py similarity index 99% rename from python/socketd/test/04_websockets.py rename to python/socketd/test/base_test/04_websockets.py index b496a2fb..fe79d115 100644 --- a/python/socketd/test/04_websockets.py +++ b/python/socketd/test/base_test/04_websockets.py @@ -50,4 +50,3 @@ class websockets_Test(unittest.TestCase): if __name__ == "__main__": websockets_Test().test_application() - pass \ No newline at end of file -- Gitee From 73e4f7f2e07b9bc369b2ae2f41a5c6434a21394d Mon Sep 17 00:00:00 2001 From: bai <1145000687@qq.com> Date: Thu, 21 Dec 2023 17:53:32 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/socketd/socketd/core/SessionDefault.py | 5 +- python/socketd/test/TestCase01.py | 23 ++++++- .../cases/TestCase03_client_session_close.py | 67 +++++++++++++++++++ .../TestCase04_sendAndRequest_timeout.py | 60 +++++++++++++++++ .../socketd/test/modelu/SimpleListenerTest.py | 8 ++- 5 files changed, 159 insertions(+), 4 deletions(-) create mode 100644 python/socketd/test/cases/TestCase03_client_session_close.py create mode 100644 python/socketd/test/cases/TestCase04_sendAndRequest_timeout.py diff --git a/python/socketd/socketd/core/SessionDefault.py b/python/socketd/socketd/core/SessionDefault.py index c9a14225..6922f920 100644 --- a/python/socketd/socketd/core/SessionDefault.py +++ b/python/socketd/socketd/core/SessionDefault.py @@ -102,5 +102,6 @@ class SessionDefault(SessionBase, ABC): .set_entity(content)), None) async def close(self): - await self.channel.send_close() - await self.channel.close() + if self.channel.is_valid(): + await self.channel.send_close() + await self.channel.close() diff --git a/python/socketd/test/TestCase01.py b/python/socketd/test/TestCase01.py index 4b586f57..15fadfc5 100644 --- a/python/socketd/test/TestCase01.py +++ b/python/socketd/test/TestCase01.py @@ -5,11 +5,12 @@ from loguru import logger from test.cases.TestCase01_client_send import TestCase01_client_send from test.cases.TestCase02_auto_reconnect import TestCase02_auto_reconnect +from test.cases.TestCase03_client_session_close import TestCase03_client_session_close +from test.cases.TestCase04_sendAndRequest_timeout import TestCase04_sendAndRequest_timeout from test.modelu.BaseTest import BaseTest class TestCase01(unittest.TestCase): - schemas = ["ws"] def __init__(self, *args, **kwargs): @@ -36,3 +37,23 @@ class TestCase01(unittest.TestCase): except Exception as e: t.on_error() raise e + + def test_Case02_client_session_close(self): + for i in range(len(TestCase01.schemas)): + t = TestCase03_client_session_close(TestCase01.schemas[i], 9000 + i) + try: + t.start() + t.stop() + except Exception as e: + t.on_error() + raise e + + def test_Case02sendAndRequest_timeout(self): + for i in range(len(TestCase01.schemas)): + t = TestCase04_sendAndRequest_timeout(TestCase01.schemas[i], 9000 + i) + try: + t.start() + t.stop() + except Exception as e: + t.on_error() + raise e diff --git a/python/socketd/test/cases/TestCase03_client_session_close.py b/python/socketd/test/cases/TestCase03_client_session_close.py new file mode 100644 index 00000000..3e5adec4 --- /dev/null +++ b/python/socketd/test/cases/TestCase03_client_session_close.py @@ -0,0 +1,67 @@ +import asyncio + +from test.modelu.BaseTestCase import BaseTestCase + +from websockets.legacy.server import WebSocketServer +from loguru import logger + +from socketd.core.Session import Session +from socketd.core.SocketD import SocketD +from socketd.core.config.ServerConfig import ServerConfig +from socketd.core.module.StringEntity import StringEntity +from socketd.transport.server.Server import Server +from test.modelu.SimpleListenerTest import SimpleListenerTest, config_handler, send_and_subscribe_test + + +class TestCase03_client_session_close(BaseTestCase): + + def __init__(self, schema, port): + super().__init__(schema, port) + self.server: Server + self.server_session: WebSocketServer + self.client_session: Session + self.loop = asyncio.get_event_loop() + + async def _start(self): + self.server: Server = SocketD.create_server(ServerConfig(self.schema).set_port(self.port)) + _simple = SimpleListenerTest() + _server = self.server.config(config_handler).listen(_simple) + self.server_session: WebSocketServer = await _server.start() + + serverUrl = self.schema + "://127.0.0.1:" + str(self.port) + "/path?u=a&p=2" + self.client_session: Session = await SocketD.create_client(serverUrl) \ + .config(config_handler).open() + await self.client_session.send_and_request("demo", StringEntity("test"), 100) + + + try: + await self.client_session.close() + await self.client_session.send("demo", StringEntity("test")) + await self.client_session.send_and_subscribe("demo", StringEntity("test"), send_and_subscribe_test, 100) + except Exception as e: + pass + await asyncio.sleep(5) + logger.info(f"counter {_simple.server_counter.get()} close_counter {_simple.close_counter.get()}") + + def start(self): + super().start() + self.loop.run_until_complete(self._start()) + + async def _stop(self): + if self.client_session: + await self.client_session.close() + + if self.server_session: + self.server_session.close() + if self.server: + await self.server.stop() + + def stop(self): + super().stop() + self.loop.run_until_complete(self._stop()) + + def on_error(self): + try: + super().on_error() + except Exception as e: + logger.error(e) diff --git a/python/socketd/test/cases/TestCase04_sendAndRequest_timeout.py b/python/socketd/test/cases/TestCase04_sendAndRequest_timeout.py new file mode 100644 index 00000000..5fb9ad30 --- /dev/null +++ b/python/socketd/test/cases/TestCase04_sendAndRequest_timeout.py @@ -0,0 +1,60 @@ +import asyncio + +from test.modelu.BaseTestCase import BaseTestCase + +from websockets.legacy.server import WebSocketServer +from loguru import logger + +from socketd.core.Session import Session +from socketd.core.SocketD import SocketD +from socketd.core.config.ServerConfig import ServerConfig +from socketd.core.module.StringEntity import StringEntity +from socketd.transport.server.Server import Server +from test.modelu.SimpleListenerTest import SimpleListenerTest, config_handler, send_and_subscribe_test + + +class TestCase04_sendAndRequest_timeout(BaseTestCase): + + def __init__(self, schema, port): + super().__init__(schema, port) + self.server: Server + self.server_session: WebSocketServer + self.client_session: Session + self.loop = asyncio.get_event_loop() + + async def _start(self): + self.server: Server = SocketD.create_server(ServerConfig(self.schema).set_port(self.port)) + _simple = SimpleListenerTest() + _server = self.server.config(config_handler).listen(_simple) + self.server_session: WebSocketServer = await _server.start() + + serverUrl = self.schema + "://127.0.0.1:" + str(self.port) + "/path?u=a&p=2" + self.client_session: Session = await SocketD.create_client(serverUrl) \ + .config(config_handler).open() + try: + await self.client_session.send("demo", StringEntity("test")) + await self.client_session.send_and_request("demo", StringEntity("test"), 100) + except Exception as e: + pass + await asyncio.sleep(5) + logger.info(f"counter {_simple.server_counter.get()} close_counter {_simple.close_counter.get()} message {_simple.message_counter.get()}") + + def start(self): + super().start() + self.loop.run_until_complete(self._start()) + + async def _stop(self): + if self.client_session: + await self.client_session.close() + + if self.server_session: + self.server_session.close() + if self.server: + await self.server.stop() + + def stop(self): + super().stop() + self.loop.run_until_complete(self._stop()) + + def on_error(self): + super().on_error() diff --git a/python/socketd/test/modelu/SimpleListenerTest.py b/python/socketd/test/modelu/SimpleListenerTest.py index 3ffbb4be..6fd74ba0 100644 --- a/python/socketd/test/modelu/SimpleListenerTest.py +++ b/python/socketd/test/modelu/SimpleListenerTest.py @@ -17,6 +17,8 @@ class SimpleListenerTest(Listener, ABC): def __init__(self): self.server_counter = AtomicRefer(0) + self.close_counter = AtomicRefer(0) + self.message_counter = AtomicRefer(0) def on_open(self, session): pass @@ -25,6 +27,8 @@ class SimpleListenerTest(Listener, ABC): with self.server_counter: self.server_counter.set(self.server_counter.get() + 1) if message.is_request(): + with self.message_counter: + self.message_counter.set(self.message_counter.get() + 1) await session.reply(message, StringEntity("reply")) await session.reply_end(message, StringEntity("ok test")) await session.reply(message, StringEntity("reply")) @@ -34,7 +38,9 @@ class SimpleListenerTest(Listener, ABC): await session.reply(message, StringEntity("reply")) def on_close(self, session): - pass + logger.debug("客户端主动关闭了") + with self.close_counter: + self.close_counter.set(self.close_counter.get() + 1) def on_error(self, session, error): pass -- Gitee From 890c4f05fb6ffb4a4f64bab54af15d4d37790d2d Mon Sep 17 00:00:00 2001 From: bai <1145000687@qq.com> Date: Thu, 21 Dec 2023 18:32:48 +0800 Subject: [PATCH 5/6] update readme --- python/socketd/README.md | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/python/socketd/README.md b/python/socketd/README.md index 754a0a58..994ce36a 100644 --- a/python/socketd/README.md +++ b/python/socketd/README.md @@ -6,13 +6,16 @@
- python3.10+ -
- -