From ee72bfd014588899d3d55ce2b282396d1abcda01 Mon Sep 17 00:00:00 2001 From: SantaSpeen Date: Thu, 6 Jul 2023 01:29:50 +0300 Subject: [PATCH] I have sockets.. --- src/core/__init__.py | 4 +- src/core/core.py | 141 +++++++++++++++++++--------------------- src/core/core.pyi | 45 +++++++++++++ src/core/tcp_server.py | 103 +++++++++++++++++++++++++++++ src/core/tcp_server.pyi | 30 +++++++++ 5 files changed, 247 insertions(+), 76 deletions(-) create mode 100644 src/core/core.pyi create mode 100644 src/core/tcp_server.py create mode 100644 src/core/tcp_server.pyi diff --git a/src/core/__init__.py b/src/core/__init__.py index acdd31e..520578a 100644 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -35,7 +35,7 @@ from modules import Console from modules import MultiLanguage from core.core import Core -loop = asyncio.get_event_loop() +loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) log = get_logger("init") @@ -88,7 +88,7 @@ builtins.config = config log.debug("Initializing console...") console = Console() console.builtins_hook() -console.logger_hook() +# console.logger_hook() console.add_command("stop", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") console.add_command("exit", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") diff --git a/src/core/core.py b/src/core/core.py index 1f6faa0..fd98872 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -5,10 +5,51 @@ # Licence: FPA # (c) kuitoi.su 2023 import asyncio +import socket import struct -from asyncio import StreamWriter, StreamReader +import traceback from core import utils +from .tcp_server import TCPServer +from .udp_server import UDPServer + + +class Client: + + def __init__(self, sock): + self.cid = 0 + self.nick = None + self.log = utils.get_logger("client") + self.addr = sock.getsockname() + self.socket = sock + self.loop = asyncio.get_event_loop() + self.alive = True + + def is_disconnected(self): + if not self.alive: + return True + try: + keep_alive = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) + if keep_alive: + return False + except OSError: + pass + self.alive = False + return True + + def kick(self, reason): + self.log.info(f"Client: \"IP: {self.addr!r}; ID: {self.cid}\" - kicked with reason: \"{reason}\"") + self.socket.send(b"K" + bytes(reason, "utf-8")) + self.socket.close() + self.alive = False + + def tcp_send(self, data): + header = b"C\x00\x00\x00\x00" + # size = len(data) + # to_send = bytearray(size + len(data)) + # to_send[0:len(data)] = size.to_bytes(len(data), byteorder='big') + # to_send[len(data):] = data + self.socket.send(header + b"\x00" + data + b"\x00") class Core: @@ -16,88 +57,40 @@ class Core: def __init__(self): self.log = utils.get_logger("core") self.clients = {} - self.loop = None + self.clients_counter = 0 + self.server_ip = config.Server["server_ip"] + self.server_port = config.Server["server_port"] + self.loop = asyncio.get_event_loop() + self.tcp = TCPServer + self.udp = UDPServer - async def tpc_send(self, data, sync): - pass + def create_client(self, *args, **kwargs): + cl = Client(*args, **kwargs) + self.clients_counter += 1 + cl.id = self.clients_counter + self.clients.update({cl.id: cl}) + return cl - async def tcp_rcv(self, writer: StreamWriter): - - sock = writer.get_extra_info('socket') - print(writer.transport) - recv = writer._loop.sock_recv - header_data = b'' - while True: - chunk = await recv(sock, 1024) - if not chunk: - break - header_data += chunk - print(header_data) - return - - async def kick_client(self, writer: StreamWriter, reason: str): - self.log.info( - f"Client: \"IP: {writer.get_extra_info('peername')!r}; Nick: {None}\" - kicked with reason: \"{reason}\"") - writer.write(b"K" + bytes(reason, "utf-8")) - await writer.drain() - writer.close() - - async def auth_client(self, writer: StreamWriter): - # TODO: Authentication - addr = writer.get_extra_info('peername') - self.log.debug(f"Client: \"IP: {addr!r}; Nick: {None}\" - started authentication!") - data = await self.tcp_rcv(writer) - self.log.info(data) - await self.kick_client(writer, "TODO") - - async def tpc_handle_client(self, reader, writer: StreamWriter): - while True: - data = await reader.read(2048) - if not data: - break - message = data.decode("utf-8").strip() - addr = writer.get_extra_info('peername') - self.log.debug(f"Received {message!r} from {addr!r}") - code = message[0] - self.log.debug(f"Client code: {code!r}") - match code: - case "C": - await self.auth_client(writer) - case "D": - # TODO: HandleDownload - await self.kick_client(writer, "TODO: HandleDownload") - case "P": - # TODO: Понять что это и зачем... - writer.write(b"P") - # writer.close() - case _: - self.log.error(f"Unknown code: {code}") - await self.kick_client(writer, "Unknown code") - await self.kick_client(writer, "Error while connecting..") - - async def tcp_part(self, host, port): - server = await asyncio.start_server(self.tpc_handle_client, host, port) - self.loop = server.get_loop() - print(f"TCP Serving on {server.sockets[0].getsockname()}") - async with server: - await server.serve_forever() - - async def udp_part(self, server_ip, server_port): - pass + async def check_alive(self): + await asyncio.sleep(5) + self.log.debug(f"Checking if clients is alive") + for cl in self.clients.values(): + d = await cl.is_disconnected() + if d: + self.log.debug(f"Client ID: {cl.id} died...") async def main(self): - server_ip = config.Server["server_ip"] - server_port = config.Server["server_port"] - self.log.info(i18n.ready) - - while True: + self.tcp = self.tcp(self, self.server_ip, self.server_port) + self.udp = self.udp(self, self.server_ip, self.server_port) + self.log.info(i18n.ready) + # while True: try: - tasks = [console.start(), self.tcp_part(server_ip, server_port), self.udp_part(server_ip, server_port)] + tasks = [console.start(), self.tcp.start(), self.udp.start()] # self.check_alive() await asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) except Exception as e: await asyncio.sleep(1) print("Error: " + str(e)) - # traceback.print_exc() + traceback.print_exc() except KeyboardInterrupt: raise KeyboardInterrupt diff --git a/src/core/core.pyi b/src/core/core.pyi new file mode 100644 index 0000000..a1f6bdf --- /dev/null +++ b/src/core/core.pyi @@ -0,0 +1,45 @@ +# Developed by KuiToi Dev +# File core.core.pyi +# Written by: SantaSpeen +# Version 0.1.2 +# Licence: FPA +# (c) kuitoi.su 2023 +import asyncio +from asyncio import StreamWriter, AbstractEventLoop, StreamReader +from asyncio.trsock import TransportSocket + +from core import utils +from .tcp_server import TCPServer +from .udp_server import UDPServer +class Client: + def __init__(self, reader: StreamReader, writer: StreamWriter): + self.cid: int = 0 + self.nick: str = None + self.log = utils.get_logger("client") + self.writer: StreamWriter = writer + self.reader: StreamReader = reader + self.addr: tuple = writer.get_extra_info('peername') + self.socket: TransportSocket = writer.get_extra_info('socket') + self.loop: AbstractEventLoop = asyncio.get_event_loop() + self.alive = True + def is_disconnected(self) -> bool: ... + def kick(self, reason: str) -> None: ... + def tcp_send(self, data: bytes) -> None: ... + + +class Core: + def __init__(self): + self.clients_counter: int = 0 + self.log = utils.get_logger("core") + self.clients = dict() + self.server_ip = config.Server["server_ip"] + self.server_port = config.Server["server_port"] + self.loop = asyncio.get_event_loop() + self.tcp = TCPServer + self.udp = UDPServer + def create_client(self, *args, **kwargs) -> Client: ... + async def check_alive(self) -> None: ... + async def main(self) -> None: ... + def start(self) -> None: ... + def stop(self) -> None: ... + diff --git a/src/core/tcp_server.py b/src/core/tcp_server.py new file mode 100644 index 0000000..6424e1f --- /dev/null +++ b/src/core/tcp_server.py @@ -0,0 +1,103 @@ +# Developed by KuiToi Dev +# File core.tcp_server.py +# Written by: SantaSpeen +# Version 0.1.2 +# Licence: FPA +# (c) kuitoi.su 2023 +import asyncio +import socket +import traceback + +from core import utils + + +class TCPServer: + def __init__(self, core, host, port): + self.log = utils.get_logger("TCPServer") + self.Core = core + self.host = host + self.port = port + + async def send(self, data, sync): + pass + + async def recv(self, client): + not_alive = client.is_disconnected() + if not not_alive: + self.log.debug(f"Client with ID {client.cid} disconnected") + return "" + data = b"" + while True: + chunk = await client.loop.sock_recv(client.socket, 10) + if not chunk: + break + data += chunk + return data + + async def auth_client(self, sock): + # TODO: Authentication + client = self.Core.create_client(sock) + self.log.debug(f"Client: \"IP: {client.addr!r}; ID: {client.cid}\" - started authentication!") + data = await self.recv(client) + self.log.debug(f"recv1 data: {data}") + if len(data) > 50: + client.kick("Too long data") + return + if "VC2.0" not in data.decode("utf-8"): + client.kick("Outdated Version.") + return + else: + self.log.debug('tcp_send(b"A")') + client.tcp_send(b"A") + + data = await self.recv(client) + self.log.debug(f"recv2 data: {data}") + + client.kick("TODO Authentication") + + async def handle_client(self, sock): + + while True: + try: + data = sock.recv(1) + if not data: + break + message = data.decode("utf-8").strip() + addr = sock.getsockname() + self.log.debug(f"Received {message!r} from {addr!r}") + code = message[0] + match code: + case "C": + await self.auth_client(sock) + case "D": + # TODO: HandleDownload + print("TODO: HandleDownload") + case "P": + # TODO: Понять что это и зачем... + sock.sendall(b"P") + case _: + self.log.error(f"Unknown code: {code}") + except Exception as e: + print("Error:", e) + traceback.print_exc() + break + print("Error while connecting..") + + # async def start(self): + # self.log.debug("Starting TCP server.") + # server = await asyncio.start_server(self.handle_client, self.host, self.port, family=socket.AF_INET) + # self.log.debug(f"Serving on {server.sockets[0].getsockname()}") + # async with server: + # await server.serve_forever() + + async def start(self): + self.log.debug("Starting TCP server.") + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.bind((self.host, self.port)) + server_socket.listen(config.Game["players"]) + self.log.debug(f"Serving on {server_socket.getsockname()}") + server_socket.setblocking(False) + loop = asyncio.get_event_loop() + while True: + sock, _ = await loop.sock_accept(server_socket) + loop.create_task(self.handle_client(sock)) diff --git a/src/core/tcp_server.pyi b/src/core/tcp_server.pyi new file mode 100644 index 0000000..699b69e --- /dev/null +++ b/src/core/tcp_server.pyi @@ -0,0 +1,30 @@ +# Developed by KuiToi Dev +# File core.tcp_server.pyi +# Written by: SantaSpeen +# Version 0.1.2 +# Licence: FPA +# (c) kuitoi.su 2023 +from asyncio import StreamWriter, StreamReader +import socket + +from core import utils, Core +from core.core import Client + + +class TCPServer: + def __init__(self, core: Core, host, port): + self.log = utils.get_logger("TCPServer") + self.Core = core + self.host = host + self.port = port + + async def send(self, data, sync) -> None: ... + + async def recv(self, writer: Client) -> bytes: ... + + async def auth_client(self, sock: socket.socket) -> None: ... + + async def handle_client(self, sock: socket.socket) -> None: ... + + async def start(self) -> None: ... +