diff --git a/09kapitel/091 Dateien.ipynb b/09kapitel/091 Dateien.ipynb new file mode 100644 index 0000000..a1d576b --- /dev/null +++ b/09kapitel/091 Dateien.ipynb @@ -0,0 +1,404 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "Unter *IO* (Input/Output) versteht man alle Mechanismen,\n", + "die mit dem Ausleiten von Daten aus einem Programm\n", + "bzw. dem Einlesen von Daten in ein Programm zusammenhängen." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 9.1 Zeichenkodierung\n", + "\n", + "Möchte ein Programm Daten senden (z.B. in eine Datei\n", + "oder über eine Netzwerkverbindung) oder diese empfangen,\n", + "werden letztendlich Bytes übertragen. Handelt es sich bei den\n", + "Daten um Text - also lesbare Zeichen - so muss durch eine\n", + "Zeichenkodierung (engl. *encoding*) festgelegt werden,\n", + "wie dieser Text auf Bytes abgebildet wird.\n", + "\n", + "Ein sehr übliches Kodierungsschema ist *UTF-8*. Darin werden\n", + "die Zahlen 0 bis 127 analog zum alten ASCII-Standard\n", + "insbesondere für grundlegende lateinische Zeichen in\n", + "Groß- und Kleinschrift verwendet. Alle übrigen Zeichen wie\n", + "z.B. deutsche Umlaute, chinesische Schriftzeichen,\n", + "Emojis etc. werden durch größere Zahlen ggf. mit mehreren\n", + "Bytes codiert. Die Zuordnung der Zahlen zu den Zeichen\n", + "ist durch den [*Unicode*-Standard](http://www.unicode.org/)\n", + "festgelegt." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Möchte man eine Python-Zeichenkette in eine Unicode-Bytefolge\n", + "übertragen, so kann dafür die Methode encode der Klasse\n", + "*str* (String) verwendet werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "text = \"Nürnberg ist schön\"\n", + "print(f\"Länge {len(text)}\")\n", + "utf8 = text.encode(\"utf-8\")\n", + "print(utf8)\n", + "print(f\"Länge {len(utf8)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Das vorangestellte b signalisiert, dass es sich\n", + "hier um eine Bytefolge handelt." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "print(type(utf8))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Eine solche Bytefolge kann auch wieder in einen\n", + "Textstring verwandelt werden. Dazu besitzt die\n", + "Klasse *bytes* die Methode decode." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "decoded_text = utf8.decode(\"utf-8\")\n", + "print(decoded_text)\n", + "print(f\"Länge {len(decoded_text)}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 9.2 Dateien\n", + "\n", + "Unter *Textdateien* verstehen wir Dateien, die\n", + "mit einem Texteditor erzeugt und bearbeitet werden können.\n", + "Der Inhalt sind lesbare Zeichen, die meist\n", + "in Zeilen organisiert sind. Um sie auf einem\n", + "Speichermedium abzulegen, müssen die Zeichen\n", + "in Bytes konvertiert bzw. beim Lesen\n", + "rückübertragen werden.\n", + "\n", + "*Binärdateien* sind Dateien, für die keine Kodierung\n", + "stattfinden soll (z.B. Bilder).\n", + "\n", + "Python besitzt bereits im Standard Funktionen, Klassen und Methoden,\n", + "die das Lesen und Schreiben von Dateien sowie das Ãœbertragen\n", + "von Text in Bytes (und zurück) unterstützen." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Einstiegspunkt ist die Built-In-Funktion open, die ein\n", + "Dateiobjekt zurückliefert. Die Parameter von open sind\n", + "der *Dateiname* (ggf. inkl. Pfad) und der *Bearbeitungsmodus*\n", + "sowie weitere, weniger gebräuchliche optionale Parameter.\n", + "\n", + "\n", + "| *Bearbeitungsmodus* | *Bedeutung* |\n", + "|:--------------------:|----------------------------------------------------|\n", + "| 'r' | Die Datei wird gelesen (muss bereits existieren) |\n", + "| 'w' | Die Datei wird geschrieben (vorhandene Datei wird überschrieben) |\n", + "| 'a' | Die Datei wird geschrieben (neuer Inhalt wird angehängt) |\n", + "| 'r+' | Die Datei wird gelesen und geschrieben |\n", + "\n", + "Standardmäßig finden die Lese- und Schreiboperationen mit der Standard-Kodierung des verwendeten Betriebssystems statt. Soll die Kodierung unterbleiben, muss an den Bearbeitungsmodus ein 'b'\n", + "(für binär) angehängt werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "f = open(\"091a Textdatei\", \"r\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Das auf diese Weise erhaltene Dateiobjekt kann für (je nach\n", + "Bearbeitungsmodus) für das Lesen und Schreiben benutzt werden.\n", + "Die Methode read ohne Angabe eines Parameter\n", + "liest den gesamten Inhalt der Datei ein\n", + "(Vorsicht: nur bei kleinen Dateien ratsam, da speicherintensiv)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "content = f.read()\n", + "print(content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Jedes geöffnete Dateiobjekt muss auch wieder geschlossen werden,\n", + "da andernfalls Betriebssystemressourcen dauerhaft belegt bleiben." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "print(f.closed)\n", + "f.close()\n", + "print(f.closed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Sollte eine Textdatei in einer anderen Kodierung als die Standard-Kodierung vorliegen, kann die gewünschte Kodierung mittels des Parameters encoding angegeben werden. Mit encoding=None wird wieder die Standard-Kodierung gewählt, was dem Standardwert und damit dem Weglassen des Parameters entspricht. Im folgenden Beispiel wird eine Textdatei geladen, die im UTF-8-Format vorliegt. Die Standard-Kodierung auf Windows weicht allerdings davon ab, wie man an den Umlauten erkennen kann." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "f = open(\"utf8_text\", \"r\", encoding=None)\n", + "content = f.read()\n", + "print(content)\n", + "f.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "### 9.3 Kontextmanager\n", + "\n", + "Die strenge Verpflichtung, jede Datei auch zu schließen, ist\n", + "gelegentlich nicht einfach zu erfüllen. Falls z.B. eine\n", + "Exception auftritt und die Abarbeitungsreihenfolge\n", + "abgebrochen wird, ist trotzdem *close* aufzurufen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "try:\n", + " f = open(\"091a Textdatei\", \"r\")\n", + " z = 1 / 0\n", + " # f.close() würde nicht erreicht!\n", + "except:\n", + " print(\"Fehler!\")\n", + "finally:\n", + " f.close()\n", + "print(f.closed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Python bietet für dieses Problem den *Kontextmanager* an. Er\n", + "initialisiert einen *Kontext* für ein Objekt, führt einen\n", + "Codeblock in diesem Kontext aus und räumt beim Verlassen\n", + "des *Kontext* belegte Ressourcen auf.\n", + "\n", + "Das Schlüsselwort für den Beginn eines *Kontext* ist with." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " print(f.read())\n", + " print(f.closed)\n", + "\n", + "print(f.closed)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Wie ist das realisiert? Tatsächlich nutzt der\n", + "*Kontextmanager* die beiden *dunder*-Methoden\n", + "\\_\\_entry\\_\\_ und \\_\\_exit\\_\\_.\n", + "Die Methode \\_\\_entry\\_\\_ wird beim Betreten des\n", + "Kontext aufgerufen, die Methode \\_\\_exit\\_\\_ beim\n", + "Verlassen oder beim Auftreten einer nicht behandelten Exception.\n", + "\n", + "Beim Datei-Objekt von Python wird in der\n", + "\\_\\_exit\\_\\_-Methode sichergestellt, dass die\n", + "Datei geschlossen wird.\n", + "\n", + "Durch Ãœberschreiben der Methoden können auch eigene Klassen\n", + "sinnvoll mit einem *Kontextmanager* zusammenarbeiten.\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/091a Textdatei b/09kapitel/091a Textdatei new file mode 100644 index 0000000..b0ba62c --- /dev/null +++ b/09kapitel/091a Textdatei @@ -0,0 +1,9 @@ +1. Zeile +2. Zeile +3. etwas längere Zeile +4. Zeile - hört das nie auf? +5. Zeile - langsam wird es langweilig +6. Zeile +7. Zeile +8. Zeile +9. letzte Zeile diff --git a/09kapitel/092 Lesen.ipynb b/09kapitel/092 Lesen.ipynb new file mode 100644 index 0000000..d1ad126 --- /dev/null +++ b/09kapitel/092 Lesen.ipynb @@ -0,0 +1,215 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "### 9.4 Dateien lesen\n", + "\n", + "Bei großen Dateien sollte vom vollständigen Einlesen mittels\n", + "read Abstand genommen werden, weil dann\n", + "der gesamte Dateiinhalt im Speicher gehalten werden muss.\n", + "Vielmehr sollte in diesem Fall der Inhalt sukzessive eingelesen\n", + "werden." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Zu diesem Zweck kann read mit einem Parameter\n", + "aufgerufen werden, der die maximale Anzahl Bytes angibt,\n", + "die gelesen werden sollen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " block_size = 10\n", + " block = f.read(block_size)\n", + "\n", + " while len(block)>0:\n", + " print(block, end='*')\n", + " block = f.read(block_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Man kann sich das schrittweise Einlesen der Datei mit Hilfe\n", + "eines Schreib-/Lesekopfs vorstellen, der sich\n", + "entlang eines Stroms von Bytes bzw. Zeichen bewegt.\n", + "Jeder Lesevorgang bewegt den Kopf um die Anzahl der\n", + "gelesenen Zeichen vorwärts. Die Position des Kopfs\n", + "innerhalb der Datei kann mittels tell\n", + "ermittelt werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " block_size = 10\n", + " block = f.read(block_size)\n", + "\n", + " while len(block)>0:\n", + " print(block, end='')\n", + " pos = f.tell()\n", + " print(f\"<{pos}>\", end='')\n", + " block = f.read(block_size)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Die Position des Schreib-/Lesekopfs lässt sich auch\n", + "durch die Methode seek verändern." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " f.seek(110)\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Textdateien sind i.d.R. in Zeilen aufgeteilt, die jeweils\n", + "mit einem Zeilenendezeichen abschließen. Die Methode\n", + "readline liest aus einer Textdatein\n", + "die Daten bis zum nächsten Zeilenende." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " line = f.readline()\n", + " while len(line)>0:\n", + " print(line, end='')\n", + " line = f.readline()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Diese häufig benötigte Variante des Lesens einer Textdatei\n", + "kann in Python auch mittels einer for-Schleife\n", + "realisiert werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"091a Textdatei\", \"r\") as f:\n", + " for line in f:\n", + " print(line, end='')" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/093 Schreiben.ipynb b/09kapitel/093 Schreiben.ipynb new file mode 100644 index 0000000..d6340f6 --- /dev/null +++ b/09kapitel/093 Schreiben.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "### 9.4 Dateien schreiben\n", + "\n", + "Voraussetzung dafür, dass in eine Datei geschrieben werden kann,\n", + "ist ein passender Bearbeitungsmodus." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"093a Textdatei\", \"w\") as f:\n", + " f.write(\"Nürnberg ist schön!\")\n", + "\n", + "with open(\"093a Textdatei\", \"r\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Wird der Schreib-/Lesekopf zurückgesetzt, kann der bestehende Inhalt überschrieben werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"093a Textdatei\", \"w\") as f:\n", + " f.write(\"Nürnberg ist nice!\")\n", + " pos = f.tell()-5\n", + " f.seek(pos)\n", + " f.write(\"schön!\")\n", + "\n", + "with open(\"093a Textdatei\", \"r\") as f:\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Durch den Bearbeitungsmodus 'r+' ist es auch\n", + "möglich, mit einem Datei-Objekt zu lesen und zu schreiben." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"093a Textdatei\", \"r+\") as f:\n", + " f.write(\"Nürnberg ist nice!\")\n", + " pos = f.tell()-5\n", + " f.seek(pos)\n", + " f.write(\"schön!\")\n", + " f.seek(0)\n", + " print(f.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Anders als bei print wird bei write\n", + "kein Zeilenwechsel angehängt; es muss also ggf. selbst\n", + "hinzugefügt werden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "with open(\"093a Textdatei\", \"r+\") as f:\n", + " f.write(\"Zeile 1\")\n", + " f.write(\"Zeile 2\\n\")\n", + " f.write(\"Zeile 3\\n\")\n", + " f.seek(0)\n", + " print(f.readline())" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/094 Uebung.ipynb b/09kapitel/094 Uebung.ipynb new file mode 100644 index 0000000..6082b70 --- /dev/null +++ b/09kapitel/094 Uebung.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "\n", + "Gegeben ist eine binäre Grafikdatei ohm.png." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "from IPython.display import Image\n", + "Image(filename=\"ohm.png\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "_Aufgabe 1_\n", + "\n", + "Erstellen Sie ein Python-Programm, dass diese Datei in eine zweite\n", + "Datei logo.png kopiert. Ãœbertragen Sie die Daten bitte\n", + "in 32 Byte Paketen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "from IPython.display import Image\n", + "\n", + "\n", + "with open(\"ohm.png\", \"rb\") as source_file:\n", + " with open(\"logo.png\", \"wb\") as dest_file:\n", + " buffer_size = 32\n", + " buffer = source_file.read(buffer_size)\n", + " while len(buffer)>0:\n", + " dest_file.write(buffer)\n", + " buffer = source_file.read(buffer_size)\n", + "\n", + "\n", + "Image(filename=\"logo.png\")" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/095 Sockets.ipynb b/09kapitel/095 Sockets.ipynb new file mode 100644 index 0000000..5237bec --- /dev/null +++ b/09kapitel/095 Sockets.ipynb @@ -0,0 +1,186 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "### 9.5 Sockets\n", + "\n", + "So wie der Begriff *Datei* eine griffige Bezeichnung für eine Ansammlung dauerhaft gespeicherter Bytes ist, wird\n", + "mit *Socket* ein Endpunkt für Netzwerkverbindungen bezeichnet.\n", + "Die Kommunikation findet zwischen zwei Sockets *bidirektional* statt, d.h. über jedes Socket können Bytes versendet\n", + "und empfangen werden." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Sockets stehen in Python durch das Standard-Modul sockets zur Verfügung. Wie schon Dateien belegen auch\n", + "Sockets Betriebssystem-Ressourcen, so dass die Verwendung eines Kontextmanagers ratsam ist." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import socket\n", + "\n", + "with socket.socket() as s:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### 9.5.1 Client-Server-Modell\n", + "\n", + "Zwar sind bei einer bestehenden Kommunikationsverbindung zwischen zwei Sockets die beiden Sockets prinzipiell\n", + "gleichberechtigt, jedoch gibt es Unterschiede beim Kommunikationsaufbau.\n", + "\n", + "* Ein *Server-Socket* verhält sich passiv, d.h. es wartet auf eingehende Verbindungsanfragen. Trifft eine solche\n", + " ein, generiert es ein weiteres Socket, das fest dieser Verbindung zugeordnet wird und über das die\n", + " anschließende Kommunikation abgewickelt wird. Das *Server-Socket* selbst verbleibt im Wartezustand.\n", + "\n", + "* Ein *Client-Socket* übernimmt den aktiven Part. Es kontaktiert das *Server-Socket* und geht selbst die\n", + " Verbindung mit dem daraufhin bereitgestellten Socket ein." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Die Verbindungsaufnahme folgt einem vorgegebenen Ablauf aus definierten Schritten (auch: *Kommunikationsprimitive*).\n", + "\n", + "![Verbindungsaufnahme mit Kommunkationsprimitiven](../img/Primitive.png \"Kommunikationsprimitive\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### 9.5.2 Server-Socket\n", + "\n", + "Ein *Server-Socket* ist eindeutig festgelegt durch das Protokoll (z.B. TCP oder UDP), den Rechner\n", + "(z.B. durch dessen IP-Adresse) und eine Portnummer. Letztere dient dazu, dass mehrere Server-Sockets gleichzeitig\n", + "auf einem Rechner existieren können, ohne sich gegenseitig zu beeinflussen.\n", + "\n", + "So gibt es z.B. auf dem Server medinf.efi.th-nuernberg.de mehrere Server-Sockets,\n", + "die permanent angesprochen werden können:\n", + "\n", + "* Port 22: Nimmt Secure-Shell-Anfragen entgegen\n", + "* Port 80: Nimmt HTTP-Anfragen entgegen\n", + "* Port 443: Nimmt HTTPS-Anfragen entgegen" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Das *Server-Socket* wird mit bind an einen bestimmten Port und eine\n", + "IP-Adresse (ggf. hat ein Rechner mehrere IP-Adressen) gebunden. Das Binden schlägt fehl, wenn die\n", + "Kombination bereits von einem anderen Socket belegt ist.\n", + "\n", + "Anschließend beginnt das *Server-Socket* mit listen zu lauschen. Das Primitiv accept\n", + "blockiert den Server, bis ein *Client-Socket* Verbindung aufnimmt. Damit entsteht ein weiteres Socket mit\n", + "einer vom System vergebenen Portnummer, das dediziert nur für die Verbindung mit diesem Client verwendet wird.\n", + "\n", + "Ãœber dieses neue Socket folgen danach Primitive zum Senden\n", + "send und Empfangen receive von Nachrichten. Anzahl und Abfolge sind\n", + "für Sockets nicht festgelegt, müssen aber zwischen Client und Server abgestimmt sein. So legt z.B.\n", + "das auf Sockets aufsetzende Protokoll HTTP fest, dass zunächst eine Nachricht vom Client\n", + "an den Server (*HTTP-Request*) und anschließend eine Nachricht vom Server an den Client\n", + "(*HTTP-Response*) gesendet wird. Während in der ersten Version von HTTP danach die\n", + "Verbindung wieder abgebaut wurde, ist es aktuellen Versionen möglich, dieses Abfolge\n", + "wiederholt über eine bestehende Verbindung abzuwickeln." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Eine etablierte Verbindung ist mit close zu schließen. Bei Verwendung eines\n", + "Kontextmanagers erfolgt dies automatisch.\n", + "\n", + "Auch das *Server-Socket* muss geschlossen werden, um das Binden an einen Port zu beenden\n", + "und den Port freizugeben." + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/096 Server.ipynb b/09kapitel/096 Server.ipynb new file mode 100644 index 0000000..d02499d --- /dev/null +++ b/09kapitel/096 Server.ipynb @@ -0,0 +1,102 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "### 9.5 Sockets" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### 9.5.3 Beispiel für einen Server" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import socket\n", + "\n", + "with socket.socket() as server: # Generieren eines Sockets\n", + " host = socket.gethostname() # Die eigene IP-Nummer\n", + " port = 12345 # Festlegen des Ports\n", + " server.bind((host, port)) # Bind\n", + " server.listen() # Listen (blockiert nicht)\n", + " print(\"Ich lausche...\")\n", + "\n", + " for contact in range(0, 10):\n", + " connection, addr = server.accept() # blockiert\n", + " with connection:\n", + " print('Verbindung von ', addr)\n", + " msg = f'Danke für das Vorbeischauen, #{contact+1}!\\n'\n", + " connection.send(msg.encode('utf-8')) # Senden \n", + "\n", + " print(\"Das wars!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Textnachrichten, die über ein Socket versendet werden sollen, müssen in Bytes\n", + "umgewandelt werden.\n" + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/097 Client.ipynb b/09kapitel/097 Client.ipynb new file mode 100644 index 0000000..6cadf7f --- /dev/null +++ b/09kapitel/097 Client.ipynb @@ -0,0 +1,146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Software Entwicklung \n", + "\n", + "## Kapitel 9: IO\n", + "\n", + "### 9.5 Sockets\n", + "\n", + "#### 9.5.4 Client-Socket\n", + "\n", + "Da das *Client-Socket* den Server kontaktiert, benötigt es die Adresse und den\n", + "Port des *Server-Sockets*. Mit connect\n", + "wird die Verbindung aufgebaut, sofern an der Gegenstelle ein Server\n", + "im Primitiv accept wartet.\n", + "\n", + "Danach folgen Primitive zum Senden\n", + "send und Empfangen receive von Nachrichten\n", + "in einer mit dem Server abgestimmten Reihenfolge.\n", + "\n", + "Abschließend ist die Verbindung mit close zu schließen. Bei Verwendung eines\n", + "Kontextmanagers erfolgt dies automatisch." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "#### 9.5.5 Beispiel für einen Client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import socket\n", + "\n", + "with socket.socket() as client: # Generieren des Sockets\n", + " host = socket.gethostname() # Server-IP\n", + " port = 12345 # Server-Port\n", + "\n", + " client.connect((host, port)) # Connect\n", + " bytes = client.recv(1024) # Receive\n", + " print (bytes.decode('utf-8'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Möchte man ein Socket wie eine Datei behandeln, also statt send und\n", + "recv lieber write und read verwenden, so kann\n", + "mit makefile eine \"Hülle\" um das Socket gelegt werden, die die\n", + "(meisten) Funktionen für Dateien anbietet und übersetzt." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import socket\n", + "\n", + "with socket.socket() as client: # Generieren des Sockets\n", + " host = socket.gethostname() # Server-IP\n", + " port = 12345 # Server-Port\n", + "\n", + " client.connect((host, port)) # Connect\n", + " with client.makefile(encoding=\"utf-8\") as file:\n", + " text = file.readline()\n", + " print (text)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "Textnachrichten, die über ein Socket empfangen werden, müssen aus den übertragenen Bytes\n", + "abgeleitet werden. Nutzt man die Dateischnittstelle, findet die Konvertierung dort statt.\n", + "\n", + " " + ] + } + ], + "metadata": { + "celltoolbar": "Slideshow", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/09kapitel/ohm.png b/09kapitel/ohm.png new file mode 100644 index 0000000..bd4d275 Binary files /dev/null and b/09kapitel/ohm.png differ diff --git a/09kapitel/rise.css b/09kapitel/rise.css new file mode 100644 index 0000000..c70f5af --- /dev/null +++ b/09kapitel/rise.css @@ -0,0 +1,22 @@ +body.rise-enabled div.inner_cell>div.input_area { + font-size: 150%; +} + +body.rise-enabled div.output_subarea.output_text.output_result { + font-size: 150%; +} +body.rise-enabled div.output_subarea.output_text.output_stream.output_stdout { + font-size: 150%; +} + +body.rise-enabled div.output_subarea.output_html.rendered_html.output_result { + font-size: 150%; +} + +body.rise-enabled td { + font-size: 120%; +} + +body.rise-enabled th { + font-size: 120%; +} \ No newline at end of file diff --git a/09kapitel/utf8_text b/09kapitel/utf8_text new file mode 100644 index 0000000..74b18ae --- /dev/null +++ b/09kapitel/utf8_text @@ -0,0 +1,5 @@ +Zeile 1 +Zeile 2 +Zeile 3 +Zeile 4 Hört das denn nie auf? +Zeile 5