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