Skip to content

Commit 6ea2815

Browse files
committed
feat: add on_connected and on_disconnected callback
1 parent 1933b66 commit 6ea2815

File tree

6 files changed

+170
-63
lines changed

6 files changed

+170
-63
lines changed

README.md

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,32 +41,59 @@ Here are some minimal example programs using the `cushy-socket`: a server that e
4141

4242
```python
4343
# echo tcp server program
44+
import socket
4445
from cushy_socket.tcp import CushyTCPServer
4546

46-
es_tcp_server = CushyTCPServer(host='localhost', port=7777)
47-
es_tcp_server.run()
47+
cushy_tcp_server = CushyTCPServer(host='localhost', port=7777)
48+
cushy_tcp_server.run()
4849

4950

50-
@es_tcp_server.on_message()
51+
@cushy_tcp_server.on_connected()
52+
def handle_on_connected(sock: socket.socket):
53+
print(f"[server decorator callback] new client connected.")
54+
print(sock)
55+
56+
57+
@cushy_tcp_server.on_disconnected()
58+
def handle_on_disconnected(sock: socket.socket):
59+
print(f"[server decorator callback] a client disconnected.")
60+
print(sock)
61+
62+
63+
@cushy_tcp_server.on_message()
5164
def handle_msg_from_client(msg: str):
52-
print(f"[server decorator callback] es_tcp_server rec msg: {msg}")
53-
es_tcp_server.send("hello, I am server")
65+
print(f"[server decorator callback] cushy_tcp_server rec msg: {msg}")
66+
cushy_tcp_server.send("hello, I am server")
67+
68+
5469
```
5570

5671
```python
5772
# echo tcp client program
5873
from cushy_socket.tcp import CushyTCPClient
5974

60-
es_tcp_client = CushyTCPClient(host='localhost', port=7777)
61-
es_tcp_client.run()
75+
cushy_tcp_client = CushyTCPClient(host='localhost', port=7777)
76+
cushy_tcp_client.run()
6277

6378

64-
@es_tcp_client.on_message()
79+
@cushy_tcp_client.on_connected()
80+
def handle_on_connected():
81+
print(f"[client decorator callback] connect to server.")
82+
83+
84+
@cushy_tcp_client.on_disconnected()
85+
def handle_on_disconnected():
86+
print(f"[client decorator callback] server disconnected.")
87+
88+
89+
@cushy_tcp_client.on_message()
6590
def handle_msg_from_server(msg: str):
66-
print(f"[client decorator callback] es_tcp_client rec msg: {msg}")
91+
print(f"[client decorator callback] cushy_tcp_client rec msg: {msg}")
92+
6793

94+
cushy_tcp_client.send("hello, here is CSTCP client")
95+
cushy_tcp_client.close()
6896

69-
es_tcp_client.send("hello, here is CSTCP client")
7097
```
7198

7299

cushy_socket/tcp.py

Lines changed: 93 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,57 @@
2020

2121
from threading import Thread
2222
from concurrent.futures import ThreadPoolExecutor
23-
from typing import List, Callable
23+
from typing import List, Callable, Optional
2424
import socket
2525
import logging
2626

2727
__all__ = ['CushyTCPClient', 'CushyTCPServer']
28-
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
28+
logger = logging.getLogger(__name__)
29+
30+
enable_log = False
31+
if enable_log:
32+
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
2933

3034

3135
class CushyTCPClient:
3236
def __init__(self, host: str, port: int):
33-
self.logger = logging.getLogger(__name__)
37+
self.logger = logger
3438
self.host = host
3539
self.port = port
3640
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
37-
self.callbacks: List[Callable] = []
3841
self.is_running = False
42+
self.executor = ThreadPoolExecutor()
43+
self._callbacks: List[Callable] = []
44+
self._disconnected_callback: Optional[Callable] = None
45+
self._connected_callback: Optional[Callable] = None
3946

4047
def run(self):
4148
"""
4249
startup CSTCP Client
4350
"""
4451
self.sock.connect((self.host, self.port))
4552
self.is_running = True
53+
if self._connected_callback:
54+
self.executor.submit(self._connected_callback)
4655
Thread(target=self._recv_thread).start()
4756

57+
def _recv_thread(self):
58+
while True:
59+
try:
60+
msg = self.sock.recv(1024).decode('utf-8')
61+
except Exception as e:
62+
self.logger.error(f"[easy-socket] Error when receiving msg from server: {e}")
63+
break
64+
if not msg:
65+
self.logger.error("[easy-socket] Server connection closed.")
66+
break
67+
self.logger.debug(f"[easy-socket] Received msg from server: {msg}")
68+
for callback in self._callbacks:
69+
self.executor.submit(callback, msg)
70+
71+
if self._disconnected_callback:
72+
self.executor.submit(self._disconnected_callback)
73+
4874
def send(self, msg: str or bytes):
4975
if type(msg) == str:
5076
self._send(msg.encode('utf-8'))
@@ -56,19 +82,8 @@ def send(self, msg: str or bytes):
5682
def _send(self, msg: bytes):
5783
self.sock.sendall(msg)
5884

59-
def _recv_thread(self):
60-
while True:
61-
msg = self.sock.recv(1024).decode('utf-8')
62-
if not msg:
63-
self.logger.error("[cushy-socket] Server connection closed.")
64-
break
65-
self.logger.info(f"[cushy-socket] Received msg from server: {msg}")
66-
67-
for callback in self.callbacks:
68-
callback(msg)
69-
7085
def listen(self, callback: Callable):
71-
self.callbacks.append(callback)
86+
self._callbacks.append(callback)
7287

7388
def on_message(self):
7489
"""
@@ -79,37 +94,54 @@ def on_message(self):
7994
----------------------------------------------------------------------
8095
from cushy_socket.tcp import CushyTCPClient
8196
82-
es_tcp_client = CushyTCPClient(host='localhost', port=7777)
83-
es_tcp_client.run()
97+
cushy_tcp_client = CushyTCPClient(host='localhost', port=7777)
98+
cushy_tcp_client.run()
8499
85100
86-
@es_tcp_client.on_message()
101+
@cushy_tcp_client.on_message()
87102
def handle_msg_from_server(msg: str):
88-
print(f"[client decorator callback] es_tcp_client rec msg: {msg}")
103+
print(f"[client decorator callback] cushy_tcp_client rec msg: {msg}")
89104
----------------------------------------------------------------------
90105
"""
106+
107+
def decorator(func):
108+
self._callbacks.append(func)
109+
return func
110+
111+
return decorator
112+
113+
def on_connected(self):
114+
def decorator(func):
115+
self._connected_callback = func
116+
return func
117+
118+
return decorator
119+
120+
def on_disconnected(self):
91121
def decorator(func):
92-
self.callbacks.append(func)
122+
self._disconnected_callback = func
93123
return func
94124

95125
return decorator
96126

97127
def close(self):
128+
self.sock.shutdown(2)
98129
self.sock.close()
99130
self.is_running = False
100131

101132

102133
class CushyTCPServer:
103134
def __init__(self, host: str, port: int):
104-
self.logger = logging.getLogger(__name__)
135+
self.logger = logger
105136
self.host = host
106137
self.port = port
107138
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
108-
# self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
109139
self.clients = set()
110-
self.callbacks: List[Callable] = []
111140
self.is_running = False
112141
self.executor = ThreadPoolExecutor()
142+
self._callbacks: List[Callable] = []
143+
self._disconnected_callback: Optional[Callable] = None
144+
self._connected_callback: Optional[Callable] = None
113145

114146
def run(self):
115147
"""
@@ -118,14 +150,15 @@ def run(self):
118150
self.sock.bind((self.host, self.port))
119151
self.sock.listen()
120152
self.is_running = True
121-
# self.executor.submit(self._accept_thread)
122153
Thread(target=self._accept_thread).start()
123154

124155
def _accept_thread(self):
125156
while True:
126157
client_sock, client_addr = self.sock.accept()
127-
self.logger.info(f"[cushy-socket] New client connected: {client_addr}")
158+
self.logger.debug(f"[cushy-socket] New client connected: {client_addr}")
128159
self.clients.add(client_sock)
160+
if self._connected_callback:
161+
self.executor.submit(self._connected_callback, client_sock)
129162
self.executor.submit(self._recv_thread, client_sock)
130163

131164
def _recv_thread(self, sock: socket.socket):
@@ -138,18 +171,21 @@ def _recv_thread(self, sock: socket.socket):
138171
try:
139172
msg = sock.recv(1024).decode('utf-8')
140173
except Exception as e:
141-
self.logger.error(f"[cushy-socket] Error when receiving msg from client: {e}")
142-
self.clients.remove(sock)
143-
sock.close()
174+
self._client_close(sock, 'error', f"[cushy-socket] Error when receiving msg from client: {e}")
144175
break
145176
if not msg:
146-
self.logger.info("[cushy-socket] Client connection closed.")
147-
self.clients.remove(sock)
148-
sock.close()
177+
self._client_close(sock, 'info', "[cushy-socket] Client connection closed.")
149178
break
150-
self.logger.info(f"[cushy-socket] Received msg from client: {msg}")
179+
self.logger.debug(f"[cushy-socket] Received msg from client: {msg}")
151180
self.executor.submit(self._callback_thread, msg)
152181

182+
def _client_close(self, sock: socket.socket, log_type: str, log_msg: str):
183+
self.logger.debug(log_msg) if log_type == 'info' else self.logger.debug(log_msg)
184+
if self._disconnected_callback:
185+
self.executor.submit(self._disconnected_callback, sock)
186+
self.clients.remove(sock)
187+
sock.close()
188+
153189
def send(self, msg: str or bytes, sock: socket.socket = None):
154190
"""
155191
send message to connected socket. You can choose specify socket client send message
@@ -169,11 +205,11 @@ def _send(self, msg: str or bytes, sock: socket.socket):
169205
raise Exception("Incorrect data type")
170206

171207
def _callback_thread(self, msg: str):
172-
for callback in self.callbacks:
173-
callback(msg)
208+
for callback in self._callbacks:
209+
self.executor.submit(callback, msg)
174210

175211
def listen(self, callback: Callable):
176-
self.callbacks.append(callback)
212+
self._callbacks.append(callback)
177213

178214
def on_message(self):
179215
"""
@@ -184,18 +220,33 @@ def on_message(self):
184220
----------------------------------------------------------------------
185221
from cushy_socket.tcp import CushyTCPServer
186222
187-
es_tcp_server = CushyTCPServer(host='localhost', port=7777)
188-
es_tcp_server.run()
223+
cushy_tcp_server = CushyTCPServer(host='localhost', port=7777)
224+
cushy_tcp_server.run()
189225
190226
191-
@es_tcp_server.on_message()
227+
@cushy_tcp_server.on_message()
192228
def handle_msg_from_client(msg: str):
193-
print(f"[server decorator callback] es_tcp_server rec msg: {msg}")
194-
es_tcp_server.send("hello, I am server")
229+
print(f"[server decorator callback] cushy_tcp_server rec msg: {msg}")
230+
cushy_tcp_server.send("hello, I am server")
195231
----------------------------------------------------------------------
196232
"""
233+
234+
def decorator(func):
235+
self._callbacks.append(func)
236+
return func
237+
238+
return decorator
239+
240+
def on_connected(self):
241+
def decorator(func):
242+
self._connected_callback = func
243+
return func
244+
245+
return decorator
246+
247+
def on_disconnected(self):
197248
def decorator(func):
198-
self.callbacks.append(func)
249+
self._disconnected_callback = func
199250
return func
200251

201252
return decorator

example/demo1_tcp_client.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,24 @@
1919

2020
from cushy_socket.tcp import CushyTCPClient
2121

22-
es_tcp_client = CushyTCPClient(host='localhost', port=7777)
23-
es_tcp_client.run()
22+
cushy_tcp_client = CushyTCPClient(host='localhost', port=7777)
23+
cushy_tcp_client.run()
2424

2525

26-
@es_tcp_client.on_message()
26+
@cushy_tcp_client.on_connected()
27+
def handle_on_connected():
28+
print(f"[client decorator callback] connect to server.")
29+
30+
31+
@cushy_tcp_client.on_disconnected()
32+
def handle_on_disconnected():
33+
print(f"[client decorator callback] server disconnected.")
34+
35+
36+
@cushy_tcp_client.on_message()
2737
def handle_msg_from_server(msg: str):
28-
print(f"[client decorator callback] es_tcp_client rec msg: {msg}")
38+
print(f"[client decorator callback] cushy_tcp_client rec msg: {msg}")
2939

3040

31-
es_tcp_client.send("hello, here is CSTCP client")
41+
cushy_tcp_client.send("hello, here is CSTCP client")
42+
cushy_tcp_client.close()

example/demo1_tcp_server.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,26 @@
1717
# Project Link: https://github.com/Undertone0809/cushy-socket
1818
# Contact Email: [email protected]
1919

20+
import socket
2021
from cushy_socket.tcp import CushyTCPServer
2122

22-
es_tcp_server = CushyTCPServer(host='localhost', port=7777)
23-
es_tcp_server.run()
23+
cushy_tcp_server = CushyTCPServer(host='localhost', port=7777)
24+
cushy_tcp_server.run()
2425

2526

26-
@es_tcp_server.on_message()
27+
@cushy_tcp_server.on_connected()
28+
def handle_on_connected(sock: socket.socket):
29+
print(f"[server decorator callback] new client connected.")
30+
print(sock)
31+
32+
33+
@cushy_tcp_server.on_disconnected()
34+
def handle_on_disconnected(sock: socket.socket):
35+
print(f"[server decorator callback] a client disconnected.")
36+
print(sock)
37+
38+
39+
@cushy_tcp_server.on_message()
2740
def handle_msg_from_client(msg: str):
28-
print(f"[server decorator callback] es_tcp_server rec msg: {msg}")
29-
es_tcp_server.send("hello, I am server")
41+
print(f"[server decorator callback] cushy_tcp_server rec msg: {msg}")
42+
cushy_tcp_server.send("hello, I am server")

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
setuptools.setup(
2222
name="cushy-socket",
23-
version="1.1.0",
23+
version="1.2.0",
2424
author="Zeeland",
2525
author_email="[email protected]",
2626
description="A lightweight socket library",

0 commit comments

Comments
 (0)