3lips/common/Message.py
2024-03-03 08:55:28 +00:00

119 lines
No EOL
3.7 KiB
Python

"""
@file Message.py
@author 30hours
"""
import socket
import threading
import asyncio
class Message:
"""
@class Message
@brief A class for simple TCP messaging using a listener and sender.
"""
def __init__(self, host, port):
"""
@brief Constructor for Message.
@param host (str): The host to bind the listener to.
@param port (int): The port to bind the listener to.
"""
self.host = host
self.port = port
self.server_socket = None
self.callback_message_received = None
def start_listener(self):
"""
@brief Start the TCP listener to accept incoming connections.
@details Ensure this function is run in a separate thread.
@return None.
"""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.bind((self.host, self.port))
self.server_socket.listen()
print(f"Listener is waiting for connections on {self.host}:{self.port}")
while True:
conn, addr = self.server_socket.accept()
thread = threading.Thread(target=self.handle_client, args=(conn, addr))
thread.start()
def handle_client(self, conn, addr):
"""
Handle communication with a connected client.
:param conn (socket.socket): The socket object for the connected client.
:param addr (tuple): The address (host, port) of the connected client.
:return None.
"""
with conn:
while True:
data = conn.recv(8096)
if not data:
break
# Process data in chunks
decoded_data = ""
while data:
chunk = data.decode()
decoded_data += chunk
data = conn.recv(8096)
# Call the callback function if set
if self.callback_message_received:
reply = asyncio.run(self.callback_message_received(decoded_data))
if reply:
# Send the reply in chunks
for i in range(0, len(reply), 8096):
conn.sendall(reply[i:i + 8096].encode())
def send_message(self, message):
"""
Send a message to the specified host and port.
:param message (str): The message to be sent.
:return generator: A generator yielding chunks of the reply.
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
client_socket.settimeout(3)
try:
client_socket.connect((self.host, self.port))
# Send the message in chunks
for i in range(0, len(message), 8096):
client_socket.sendall(message[i:i + 8096].encode())
# Indicate the end of transmission
client_socket.shutdown(socket.SHUT_WR)
# Receive the reply in chunks
while True:
data = client_socket.recv(8096)
if not data:
break
yield data.decode()
except ConnectionRefusedError:
print(f"Connection to {self.host}:{self.port} refused.")
def set_callback_message_received(self, callback):
"""
@brief Set callback function when a message is received.
@param callback (function): The callback function.
@return None.
"""
self.callback_message_received = callback
def close_listener(self):
"""
@brief Close the listener socket.
@return None.
"""
if self.server_socket:
self.server_socket.close()