First commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.csv
|
||||
29
client/SocketClient.py
Normal file
29
client/SocketClient.py
Normal file
@@ -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
|
||||
42
client/TCPClient.py
Normal file
42
client/TCPClient.py
Normal file
@@ -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()
|
||||
53
client/UDPClient.py
Normal file
53
client/UDPClient.py
Normal file
@@ -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)
|
||||
BIN
client/__pycache__/SocketClient.cpython-314.pyc
Normal file
BIN
client/__pycache__/SocketClient.cpython-314.pyc
Normal file
Binary file not shown.
BIN
client/__pycache__/TCPClient.cpython-314.pyc
Normal file
BIN
client/__pycache__/TCPClient.cpython-314.pyc
Normal file
Binary file not shown.
BIN
client/__pycache__/UDPClient.cpython-314.pyc
Normal file
BIN
client/__pycache__/UDPClient.cpython-314.pyc
Normal file
Binary file not shown.
63
client/main.py
Normal file
63
client/main.py
Normal file
@@ -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()
|
||||
56
server.py
Normal file
56
server.py
Normal file
@@ -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)
|
||||
Reference in New Issue
Block a user