From 1f6b38c89478ca177bd287540de8b5710910ee57 Mon Sep 17 00:00:00 2001 From: Yui Date: Sat, 2 May 2026 20:54:01 -0300 Subject: [PATCH] First commit --- .gitignore | 1 + client/SocketClient.py | 29 ++++++++ client/TCPClient.py | 42 ++++++++++++ client/UDPClient.py | 53 +++++++++++++++ .../__pycache__/SocketClient.cpython-314.pyc | Bin 0 -> 2406 bytes client/__pycache__/TCPClient.cpython-314.pyc | Bin 0 -> 3739 bytes client/__pycache__/UDPClient.cpython-314.pyc | Bin 0 -> 4426 bytes client/main.py | 63 ++++++++++++++++++ server.py | 56 ++++++++++++++++ 9 files changed, 244 insertions(+) create mode 100644 .gitignore create mode 100644 client/SocketClient.py create mode 100644 client/TCPClient.py create mode 100644 client/UDPClient.py create mode 100644 client/__pycache__/SocketClient.cpython-314.pyc create mode 100644 client/__pycache__/TCPClient.cpython-314.pyc create mode 100644 client/__pycache__/UDPClient.cpython-314.pyc create mode 100644 client/main.py create mode 100644 server.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afed073 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.csv diff --git a/client/SocketClient.py b/client/SocketClient.py new file mode 100644 index 0000000..0af8236 --- /dev/null +++ b/client/SocketClient.py @@ -0,0 +1,29 @@ +import socket as s +from logging import Logger +from abc import ABC, abstractmethod + + +class SocketClient(ABC): + ip: str = "" + port: int = 0 + socket: s.socket + logger: Logger + + def __init__(self, ip: str, port: int, logger: Logger): + if port <= 0 or port > 65535: + raise ValueError("Port out of range.") + self.ip = ip + self.port = port + self.logger = logger + + @abstractmethod + def Connect(self) -> bool: + pass + + @abstractmethod + def Reconnect(self) -> bool: + pass + + @abstractmethod + def Send(self, data: str): + pass diff --git a/client/TCPClient.py b/client/TCPClient.py new file mode 100644 index 0000000..c892261 --- /dev/null +++ b/client/TCPClient.py @@ -0,0 +1,42 @@ +import socket as s +from SocketClient import SocketClient +from select import select + + +class TCPClient(SocketClient): + def Connect(self) -> bool: + self.socket = s.socket(s.AF_INET, s.SOCK_STREAM) + try: + self.socket.connect((self.ip, self.port)) + except ConnectionRefusedError: + self.logger.fatal("TCP_FATAL_CONNECT %s:%d" % (self.ip, self.port)) + return False + + self.socket.setblocking(False) + return True + + def Reconnect(self) -> bool: + self.socket.close() + return self.Connect() + + def Send(self, data: str): + try: + self.socket.sendall(data.encode()) + self.logger.info("TCP_SENT %s" % data) + self.Receive() + except Exception as ex: + self.logger.fatal("TCP_FATAL_SEND %s" % ex) + + def Receive(self): + (incoming, _, _) = select([self.socket], [], [], 0) + if incoming: + try: + data = self.socket.recv(1024) + if not data: + self.socket.close() + self.logger.error("TCP_NO_DATA") + return + self.logger.info("TCP_SUCCESS %s" % data.decode()) + except InterruptedError as ex: + self.logger.error("TCP_FAIL %s" % ex) + self.Reconnect() diff --git a/client/UDPClient.py b/client/UDPClient.py new file mode 100644 index 0000000..ffdb1d2 --- /dev/null +++ b/client/UDPClient.py @@ -0,0 +1,53 @@ +from SocketClient import SocketClient +import time +import socket +import select + + +class UDPClient(SocketClient): + _timestamps: set[float] = set() + _last_check = time.time() + + def Connect(self) -> bool: + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.socket.setblocking(False) + return True + + def Send(self, data: str): + now = time.time() + data = "%s/%f" % (data, now) + self.socket.sendto(data.encode(), (self.ip, self.port)) + self.logger.info("UDP_SENT %s" % data) + self._timestamps.add(now) + print(self._timestamps) + self.Receive() + self.staleCheck() + + def Reconnect(self) -> bool: + return True + + def Receive(self): + (incoming, _, _) = select.select([self.socket], [], [], 0) + if incoming: + data, addr = self.socket.recvfrom(1024) + timestamp = data.split(b"/")[1] + print(timestamp.decode()) + self.logger.info("UDP_SUCCESS %s (%s)" % (data.decode(), addr)) + self._timestamps.discard(float(timestamp.decode())) + print(self._timestamps) + + def staleCheck(self): + if (time.time() - self._last_check) < 1: + return + + timedout_timestamps = set() + for timestamp in self._timestamps: + if (self._last_check - timestamp) < 10: + continue + self.logger.error("UDP_FAIL %f" % timestamp) + print(self._timestamps) + timedout_timestamps.add(timestamp) + + self._last_check = time.time() + for timestamp in timedout_timestamps: + self._timestamps.discard(timestamp) diff --git a/client/__pycache__/SocketClient.cpython-314.pyc b/client/__pycache__/SocketClient.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..650f42edfeac4a3fed562f422215ac73054138ba GIT binary patch literal 2406 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 UeMV00JI*k{b@!J9Dr@b(08@9KYybcN literal 0 HcmV?d00001 diff --git a/client/__pycache__/UDPClient.cpython-314.pyc b/client/__pycache__/UDPClient.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a6bbd3927e6944320c60ad45995691c84b8d6cf GIT binary patch literal 4426 zcmbtXU2GKB6}~h3_RlEU?0*^iC&d$!*T@~71 z?Vfw*+;h*lKj%B=-jTZ68Up2?KmAJkVUUo&FChk?A#Et5Do%OCy;At~T?k?(L9U`q4c_eq57C%5@WC~pO+YA(_2ZO-za)Ah8( zTfqgLW)h}Vt0>8I!Z1==(o_@!rS~mcQ;`dLMvYur(jrrOaw!8_X+)@+(~U^(l6gVT zMzX4TQKySzrfQgD=Zr{_?RTU+=keSnOH`CZHmjQnQ&kkJ)$Q~0LtxQp0rGsAJR`n9 z%M&DPcC`(B!j_K>EPnXILWS+$Kx3JlMjk73PQz38dHbEy_w;*4quyO5S{e~2*>X>u zRI)QyRTd~WWD+eMSuc1toG3qiQu13tgSn?wb$nWh&7L@E)y#i;az>eYTR#3RD`cqV zxiko9+4Fc-hMHb5P|Fal18P-G>e;NCG_6K=FH0=8jR!$!nLKRm`Y7{Z=9B4P&;Dxm z_tVAbiG1|LM(gy7|7vJU5U+)zipKjQii6Oya44v-77CW&#lC zGfWCR#15?mQgSM`1X_>W1|ax%&s_*~JRv~VnptJf$a`WKE!erQ!+yN@+#M9>c$s{+ z=^#G}(Z?w{4o=alyVNn~L?lNL>Nr>FY#r^O2UotU0GCC*o$Q0W<4?T+*-DXR4)^cG zXha4VE{DKjmH891C&PortW<0HO)aBRoGc(h&8AG<3aZ(po>DDd%US-MPE9MA*3X|; zspZ$Q3%V6jaExIlGC9NYB~mFXkfU1Gw5nt^scP@4Rt2?&o{qfG zu^PQOu{Lqz=z8?iL$?pznfg5TS?uocv4ZeMS*$HDw5@ipw`{hL6@+nM7X@S$g|58N zwK`uA`W_0kE2&~#Prk0FAoSuu@rro04jc7X>hB5d|0gynMkb2x_+mtMfRu|!4kIy; zwJ59Hd4Lov7?&6#5n9VcCJ^JeleY;)xA8`;YB--#+-i1ryv$@~5qisHM=SykE4Tt? z=lM$^AN;lgw#T_~G7k5OsNYt45j5twUXtVE+}CUtN&OiUXyx&hVuQ>%Xt@X$X*BMu z6f5L1`p$BrxYkn11x{Bx?Ra1g>hmDtv zE1p>LQuB9hcvfC9in)?{-fHi@*}2wPY#+vHJdMR8w-dq`9qn?a;mOI?!Txap-bh@0E9UUx#donugC@T$QS_NG5m2JfN z9ad^wD>HB~Fs4E-uY@6)2TdO9uD71X>kccR%_HG(T}ilCRLJ(;Og(d+kZRK7y}>$a zJhl58fmk@C z+@#?qc+_b876?EIoIUn_?1P!rfnvvSzGHY(99~ZpLXrDo(?5FpZm4&KPJmLeT`IIo zE3vJHmSRIV-w?iaWV2!P4qvE$^?pNJ88O{?v3oVMu5I=lDu{L`(lO?u$T=&BXPDi{ z_Sb<_e$GV*KIh=SBY#ZrGB*Tc@MXd7-)!rlJq1iU0p!IQzY3)6jQ<|_5xL*k^0QOF z=*au~pZW;LvAKedNz{iowOOgb5)>ty$l!CXMo}_)YAKCvc%DE5qSuFPp{g?M+Qqexysf&r7V({A)U4i}cPNl?EB9;Wb!)RIcVG^uuO2_Bum z2Fdc%HL0f4iXw6Lg9A7BO*;}}?4U^4THl7o^Y_4K?xSBU&ulfdek6S;{d{D3=9wR! zosaSD`K$&WB9HMk@~j0eK#%bj^fV|02A;Om1r9tr&cWvVn6T|(nxz266SNnH8%6Of zSr0MLdO&L7_hoRLJ8)h6ia^DN{|l2) B{7(P? literal 0 HcmV?d00001 diff --git a/client/main.py b/client/main.py new file mode 100644 index 0000000..5d832fd --- /dev/null +++ b/client/main.py @@ -0,0 +1,63 @@ +import logging as log +import time +import datetime +from TCPClient import TCPClient +from UDPClient import UDPClient + +IP = "10.69.0.2" +TCP_PORT = 6069 +UDP_PORT = 5069 + + +class SuperGreatFormatter(log.Formatter): + def formatTime(self, record, datefmt=None): + if not datefmt: + return super().formatTime(record, datefmt=datefmt) + + return ( + datetime.datetime.fromtimestamp(record.created) + .astimezone() + .strftime(datefmt) + ) + + +def setupLogger() -> log.Logger: + logger = log.getLogger("client") + logger.setLevel(log.DEBUG) + + filehandler = log.FileHandler( + filename="client-%s.csv" % datetime.datetime.now().isoformat(), + encoding="utf-8", + mode="a+", + ) + filehandler.setFormatter( + SuperGreatFormatter( + "{message},{levelname},{asctime}", "%Y-%m-%d %H:%M:%S.%f %z", "{" + ) + ) + logger.addHandler(filehandler) + + streamhandler = log.StreamHandler() + streamhandler.setFormatter( + log.Formatter("[{asctime}]{levelname}: {message}", "%Y-%m-%d %H:%M:%S", "{") + ) + logger.addHandler(streamhandler) + + return logger + + +def main(): + logger = setupLogger() + tcp = TCPClient(IP, TCP_PORT, logger) + udp = UDPClient(IP, UDP_PORT, logger) + tcp.Connect() + udp.Connect() + + while True: + # tcp.Send("meow") + udp.Send("woof") + time.sleep(9) + + +if __name__ == "__main__": + main() diff --git a/server.py b/server.py new file mode 100644 index 0000000..5281d9d --- /dev/null +++ b/server.py @@ -0,0 +1,56 @@ +import socket +import select + +IP = "0.0.0.0" +UDP_PORT = 5069 +TCP_PORT = 6069 + +udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +udp.bind((IP, UDP_PORT)) +tcp.bind((IP, TCP_PORT)) +tcp.listen(10) + +tcpconns = {tcp} + + +def udp_data(udp: socket.socket) -> None: + (udp_incoming, _, _) = select.select([udp], [], [], 0) + if udp_incoming: + udp_data, udp_addr = udp.recvfrom(1024) + print("Received UDP data from %s with content %s" % (udp_addr, udp_data)) + udp.sendto(udp_data, udp_addr) + + +def tcp_data(tcp: socket.socket) -> None: + (newsocks, _, _) = select.select(tcpconns, [], tcpconns, 0) + for sock in newsocks: + if sock is tcp: + conn, addr = tcp.accept() + conn.setblocking(False) + tcpconns.add(conn) + print("TCP connection from %s:%d" % addr) + print(len(tcpconns)) + continue + try: + data = sock.recv(1024) + print("TCP Data: %s" % data) + sock.sendall(b"aarf") + except socket.timeout: + print("Connection closed by remote end") + tcpconns.remove(sock) + sock.close() + except ConnectionResetError: + print("Connection reset by peer") + tcpconns.remove(sock) + sock.close() + except BrokenPipeError: + print("Broken pipe") + tcpconns.remove(sock) + sock.close() + + +while True: + udp_data(udp) + tcp_data(tcp)