Python erlaubt Verbindungen über Sockets, also auf der Transportschicht.
- Man muss den Zielhost und den gewünschten Port eingeben
- Es werden Bytes übertragen. Strings müssen codiert/decodiert werden.
# Quote-of-the-Day-Client
Eine der einfachsten Anwendungsprotokolle:
Der Client verbindet, der Server schickt ein Zitat und trennt die Verbindung.
import socket
# Verbindungseinstellungen
HOST = "djxmmx.net"
PORT = 17
# Socket erstellen und Verbindung aufnehmen
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST , PORT))
# Daten empfangen
reply = s.recv(1024).decode()
print(reply)
2
3
4
5
6
7
8
9
10
11
12
13
14
- Zeile 1
- Das Paket
socket
wird importiert. - Zeile 4
- Die Konstante
HOST
wird auf den Hostnamen oder eine IP-Adresse gesetzt. - Zeile 5
- Die Konstante
PORT
wird auf den Wert17
gesetzt (gemäss QOTD-Protokoll) - Zeile 8
- Es wird ein
socket
-Objekts
erstellt. Zum Erstellen müssen zwei Konstanten mitgeliefert werden, welche den Socket-Typ definieren:socket.AF_INET
besagt dass es sich um eine IPv4-Adresse handelt.socket.SOCK_STREAM
gibt an dass es sich um einen TCP-Socket handelt.
- Zeile 9
- mit
connect()
wird die Verbindung hergestellt. - Zeile 12
recv()
empfängt Daten. Bytes können mitdecode()
in String umgewandelt werden.- Zeile 14
- Zur Kontrolle wird der empfangene Text in der Shell ausgegeben.
Aufgabe
- Teste das obenstehendene Programm
- Stelle den Server auf
admin
um - Programmiere selbst einen Daytime-Client (überlege dir was du ändern musst – es ist nicht viel!)
# Echo-Client
Im Gegensatz zum obigen Beispiel, muss der Client beim Echo-Protokoll zuerst etwas an den Server schicken, bevor dieser dann antwortet.
import socket
# Verbindungseinstellungen
HOST = "admin.ad.kinet.ch"
PORT = 7
# Socket erstellen und Verbindung aufnehmen
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST , PORT))
# Daten schicken
s.sendall("Hallo Welt!".encode())
# Daten empfangen
reply = s.recv(1024).decode()
print(reply)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- Zeile 12
sendall()
schickt Daten. Text kann mitencode()
in Bytes umgewandelt werden.
# GUI - graphical user interface
Wir haben mit dem Mu-Editor und Pygame Zero programmieren gelernt. Wir werden versuchen mit Pygame Zero ein Benutzerinterface zu erstellen für den Chat-Client. Dabei wollen wir aber den etwas fortgeschrittenen Editor Thonny verwenden.
(Bei uns am Gymer installiert, hier (opens new window) erhältlich)
Das einfachste Pygame Zero-Programm sieht nun wie folgt aus:
import pgzrun
WIDTH = 800
HEIGHT = 600
def draw():
screen.clear()
screen.draw.circle((400, 300), 30, 'white')
pgzrun.go()
2
3
4
5
6
7
8
9
10
Im Gegensatz zum ersten Programm im Python-Kapitel, sind nur die erste und die letzte Zeile hinzugekommen.
# Tastatureingaben
In Pygame Zero haben wir die Funktion on_key_down(key, mod, unicode)
um auf Tasteneingaben zu reagieren. Wir müssen uns den Text aber selber zusammensetzen – die Funktion wird ja bei jedem einzelnen Tastendruck augerufen.
import pgzrun
WIDTH = 800
HEIGHT = 600
line = ""
def draw():
screen.clear()
screen.draw.text(line, (50, 30), color="orange")
screen.draw.circle((400, 300), 30, 'white')
def on_key_down(key, mod, unicode):
global line
line = line + unicode
pgzrun.go()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Was ist aber wenn wir uns vertippen? Wenn wir Backspace drücken, wird der Unicode für Backspace angehängt, es wird aber nicht das letzte Zeichen gelöscht. Also müssen wir bevor wir einfach anhängen, die Taste überprüfen. Die Funktion on_key_down
könnte also wie folgt aussehen:
def on_key_down(key, mod, unicode):
global line
if key == keys.BACKSPACE:
line = line[:-1]
else:
line = line + unicode
2
3
4
5
6
Aufgabe
- Teste das obenstehende Beispiel
- Baue die Löschfunktion mit Backspace ein
- Verschönere deinen Text
https://pygame-zero.readthedocs.io/en/stable/ptext.html (opens new window)
# Threads
Das folgende Beispiel basiert auf dem obenstehenden Beispiel (GUI-Tastatureingaben). Die zusätzlichen Zeilen sind hervorgehoben und werden unterhalb diskutiert.
import pgzrun
import threading
import time
WIDTH = 800
HEIGHT = 600
line = ""
clear_line = False
def draw():
screen.clear()
screen.draw.text(line, (50, 30), color="orange")
screen.draw.circle((400, 300), 30, 'white')
def update():
global clear_line, line
if clear_line:
line = ""
clear_line = False
def on_key_down(key, mod, unicode):
global line
line = line + unicode
def thread_function():
global clear_line
while True:
time.sleep(7)
print("sleep")
clear_line = True
thread = threading.Thread(target=thread_function, args=())
thread.start()
pgzrun.go()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
- Zeilen 35 und 36
- Ein neuer Thread wird erzeugt, basierend auf der Funktion
thread_function
). Der Thread wird gestartet. - Zeilen 27-32
- Diese Funktion soll in einem eigenen Thread ablaufen. Der Rest des Programms wird nicht blockiert.
- Alle 7 Sekunden wird eine Variable
clear_line
aufTrue
gesetzt. - Zeile 9
clear_line
wird definiert- Zeilen 16-20
- Die Update-Funktion ist Teil von Pygame Zero und wird regelmässig aufgerufen. Sie löscht die Zeile
line
, wennclear_line
gesetzt ist. - wenn wir
line
direkt inthread_function
verändern, merkt Pygame Zero nicht, dass was geändert worden ist und somit wirddraw
nicht sofort aufgerufen. - Zeile 2 und 3
threading
undtime
werden importiert
# String-Operationen
messages = [
"/ERROR asdfasdf",
"/JOINED asdfasdf",
"/LEFT adsfasdf",
"@me asdfasdf",
"asdfasdf asdf"
]
def receive(message):
if message.startswith("/"):
print("Status-Nachricht von Server")
status, message = message.split(maxsplit=1)
print(status)
print(message)
elif message.startswith("@me"):
print("private Nachricht")
print(message.replace("@me ", ""))
else:
print("normale Chat-Nachricht")
print(message.strip())
print("")
for message in messages:
receive(message)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# startswith
str.startswith(prefix)
Liefert True
falls str
mit prefix
beginnt. Andernfalls False
.
Liefert eine Kopie von str
, worin old
durch new
ersetzt wurde
Beispiel
str = "Guten Morgen"
if str.startswith("Guten"):
print("Ihnen auch!")
2
3
Ihnen auch!
# replace
str.replace(old, new)
Liefert eine Kopie von str
, worin old
durch new
ersetzt wurde
Beispiel
str = "Guten Morgen"
result = str.replace("Morgen", "Abend")
print(result)
2
3
Guten Abend
# strip
str.strip()
Liefert eine Kopie von str
ohne vorangestellte oder hintenangestellte Leerzeichen
Beispiel
str = " Hallo du "
result = str.strip()
print(result)
2
3
Hallo du
# split
str.split()
Liefert eine Liste von «Wörter», die im ursprünglichen String str
durch Leerschläge getrennt waren. Das Argument maxsplit
gibt an wie oft geteilt wird.
Beispiel
str = "Guten Morgen zusammen"
result = str.split(maxsplit=1)
for item in result:
print(item)
2
3
4
Guten
Morgen zusammen