Der Teil, der die Client-Verbindung bedient, sollte einfach in eine Funktion ausgelagert werden. Dann hat man auch gleichzeitig die Vorbereitung mehr als eine Verbindung gleichzeitig zu bedienen (auch wenn es nicht notwendig ist).
Basierend auf dem, was blackjack geschrieben hat:
Code: Alles auswählen
#!/usr/bin/env python3
import socket
from serial import Serial
def sende_sequenz(serial_connection, daten):
# f-strings sind schneller bei der Konstruktion
sende_text = f"{daten}\n"
print(f"Daten werden übertragen {daten}")
serial_connection.write(sende_text.encode())
serial_connection.flush()
def handle_client(client_socket, serial_connection):
with client_socket:
client_connection = client_socket.makefile(
"rw", encoding="utf-8"
)
for line in client_connection:
print(f"Daten Empfangen: {line}", end="")
if line == "test\n":
sende_sequenz(serial_connection, "o1")
wert = next(serial_connection).decode("ascii")
serial_connection.reset_output_buffer()
print("Empfangen", wert)
print("Befehl ausgeführt!\n")
client_connection.write(line)
client_connection.flush()
def main():
# socket.AF_INET, socket.SOCK_STREAM ist der Standard und kann auch entfallen
# kann sich aber ändern, wenn IPv6 mal endlich überall eingesetzt wird
with socket.socket() as server_socket:
server_socket.bind(("192.168.2.15", 50_000))
server_socket.listen(1)
print("Server gestartet!\n")
with Serial("/dev/ttyUSB0") as serial_connection:
while True:
client_socket, _ = server_socket.accept()
# hier wird jetzt die Funktion client_connections aufgerufen
# und blockiert so lange, bis die client-verbindung beendet ist
# client_socket und serial_connection müssen an die Funktion übergeben
# werden, da die Objekte in der Funbktion main() durch die Funktion
# client_connections nicht sichtbar sind. Sie sind in einem anderen Scope.
handle_client(client_socket, serial_connection)
# client-verbindung beendet, hier gehts jetzt weiter
if __name__ == "__main__":
main()
Das könnte man einfacher als Klasse haben:
Code: Alles auswählen
#!/usr/bin/env python3
import socket
from serial import Serial
class Server:
def __init__(self, ip, port, serial_port):
self.server_socket = socket.socket()
self.server_socket.bind((ip, port))
self.server_socket.listen(1)
self.serial_connection = Serial(serial_port)
print("Socket und serielle Verbindung geöffnet")
def __enter__(self):
return self
def __exit__(self, exc_type, exc_obj, exc_tb):
self.server_socket.close()
self.serial_connection.close()
print("Socket und serielle Verbindung geschlossen")
def sende_sequenz(self, daten):
# f-strings sind schneller bei der Konstruktion
sende_text = f"{daten}\n"
print(f"Daten werden übertragen {daten}")
self.serial_connection.write(sende_text.encode())
self.serial_connection.flush()
def handle_client(self, client_socket):
with client_socket:
client_connection = client_socket.makefile(
"rw", encoding="utf-8"
)
for line in client_connection:
print(f"Daten Empfangen {line}", end="")
if line == "test\n":
self.sende_sequenz("o1")
wert = next(self.serial_connection).decode("ascii")
self.serial_connection.reset_output_buffer()
print("Empfangen", wert)
elif line == "QUIT\n":
# Befehl QUIT beendet diese Schleife
break
print("Befehl ausgeführt!\n")
client_connection.write(line)
client_connection.flush()
def run(self):
while True:
client_socket, (ip, _) = self.server_socket.accept()
print(f"Neue Client-Verbindung von {ip}, die nun abgearbeitet wird")
self.handle_client(client_socket)
print("Client Verbindung beendet")
def main():
Server("0.0.0.0", 50_000, "/dev/pts/1").run()
if __name__ == "__main__":
main()
Für den Test habe ich einfach /dev/pts/1 verwendet, was das Pseudo-Terminal ist.
Das sendet natürlich nicht die erwarteten Daten zurück und der Prozess bleibt so lange stehen, bis er eine Antwort bekommt.
D.h. wenn bei dir /dev/ttyUSB0 nicht antwortet, bleibt das Programm einfach stehen.
Abfangen kann man dieses Problem, indem man mit Timeouts arbeitet.
Der Server-Socket bekommt keinen Timeout, da dieser auf die Verbindung des Clients warten muss und beim accept einen TimeoutError auslösenn würde.
Der Client-Socket sollte einen Timeout bekommen, dass falls sich dieser aufhängt, der Server-Prozess den Client wieder trennt.
Die Serielle Verbindung bekommt einen Timeout, damit sich der Server-Prozess nicht aufhängt, falls der serielle Client nicht mehr antwortet.
Basierend auf dem letzten Beispiel mit der Klasse:
Code: Alles auswählen
#!/usr/bin/env python3
import socket
from serial import Serial
class Server:
def __init__(self, ip, port, serial_port, timeout):
self.server_socket = socket.socket()
self.server_socket.bind((ip, port))
self.server_socket.listen(1)
self.serial_connection = Serial(serial_port, timeout=timeout)
self.timeout = timeout
print("Socket und serielle Verbindung geöffnet")
def __enter__(self):
return self
def __exit__(self, exc_type, exc_obj, exc_tb):
self.server_socket.close()
self.serial_connection.close()
print("Socket und serielle Verbindung geschlossen")
def sende_sequenz(self, daten):
# f-strings sind schneller bei der Konstruktion
sende_text = f"{daten}\n"
print(f"Daten werden übertragen {daten}")
self.serial_connection.write(sende_text.encode())
self.serial_connection.flush()
def handle_client(self, client_socket):
with client_socket:
client_socket.settimeout(self.timeout)
client_connection = client_socket.makefile(
"rw", encoding="utf-8"
)
for line in client_connection:
print(f"Daten Empfangen {line}", end="")
if line == "test\n":
self.sende_sequenz("o1")
wert = next(self.serial_connection).decode("ascii")
self.serial_connection.reset_output_buffer()
print("Empfangen", wert)
elif line == "QUIT\n":
# Befehl QUIT beendet diese Schleife
break
print("Befehl ausgeführt!\n")
client_connection.write(line)
client_connection.flush()
def run(self):
while True:
client_socket, (ip, _) = self.server_socket.accept()
print(f"Neue Client-Verbindung von {ip}, die nun abgearbeitet wird")
try:
self.handle_client(client_socket)
except TimeoutError:
print("Client hat nicht schnell genug geantwortet")
except StopIteration:
# durch den Aufruf über die funktion next() wird bei
# Serial() eine StopIteration final ausgelöst wenn die Verbindung
# geschlossen wird oder ein TimeoutError kommt.
print("Serielles Endgerät antwortete nicht schnell genug")
else:
print("Client Verbindung normal beendet")
def main():
Server("0.0.0.0", 50_000, "/dev/pts/1", timeout=10).run()
if __name__ == "__main__":
main()
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server