PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Ähnliche Dokumente
Tafelübung zu BS 4. Interprozesskommunikation

Projekt: Web-Server. Foliensatz 9: Projekt Folie 1. Hans-Georg Eßer, TH Nürnberg Systemprogrammierung, Sommersemester 2014

Projekt: Web-Proxy. Foliensatz 9: Projekt Folie 1. Hans-Georg Eßer, TH Nürnberg Systemprogrammierung, Sommersemester 2015

Computer and Communication Systems (Lehrstuhl für Technische Informatik) Socket-Programmierung

Operating Systems Principles. Event Queue

PROGRAMMIEREN MIT UNIX/Linux-SYSTEMAUFRUFEN

J-1 Überblick. 1 Binden von Sockets. Besprechung 6. Aufgabe (jsh) Domain, z. B. (PF_ = Protocol Family) Byteorder bei Netzwerkkommunikation

7. Tafelübung. Socket - Erzeugung. Netzwerkkommunikation und Byteorder. Binden von Sockets. Lösung der jsh-aufgabe. Erläuterung der rshd-aufgabe

Byteorder bei Netzwerkkommunikation Netzwerkprogrammierung - Sockets Netzwerkprogrammierung - Verschiedenes

Michael Golm, Universität Erlangen-Nürnberg, IMMD 4, 1999/ / Tafelübung 7. Netzwerkkommunikation und Byteorder

Rechnernetze II SS Betriebssysteme / verteilte Systeme rolanda.dwismuellera@duni-siegena.de Tel.: 0271/ , Büro: H-B 8404

Rechnernetze II WS 2012/2013. Betriebssysteme / verteilte Systeme rolanda.dwismuellera@duni-siegena.de Tel.: 0271/ , Büro: H-B 8404

1 Communication Domain und Protokoll

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Rechnernetze & Verteilte Systeme (T) Netzprogrammierung/Sockets (P)

Internetanwendungstechnik (Übung)

Interprozesskommunikation (IPC)

Kommunikationsnetze. 2. Direkte TCP/IP-Verbindungen 2.1 Höhere Programmiersprachen

Lösung Übungszettel 6

Attribute: Name (Zuweisung eines Namens durch Binding) Communication Domain Duplizieren von Filedeskriptoren. SoS I - Ü

U7-2 Netzwerkkommunikation und Byteorder

Netzwerk-Programmierung in C

Client/Server-Systeme

Client/Server-Systeme

U8-3 Netzwerkkommunikation und Byteorder. U8-2 Evaluation. U8-4 Sockets. U8-1 Überblick. Wiederholung: Byteorder a2 b5 c8 0x14a2b5c8

An Open Interface for Network Programming under Microsoft Windows. DI. Dr. Peter René Dietmüller

Betriebssysteme. Kommunikation von Prozessen und Threads. Sommersemester Prof. Dr. Peter Mandl. Seite 1. Prof. Dr. Peter Mandl.

1 Client-Server-Modell

Client/Server-Systeme

Kommunikation von Prozessen und Threads

Netzwerkprogrammierung mit Sockets und C. Ulrich Vogel

UDP auf Basis der BSD-Sockets 273

Einführung in die Netzwerkprogrammierung mit Sockets und C

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Verteilte Systeme - Java Networking (Sockets) -

21 Netzwerkprogrammierung mit Sockets

Adressauflösung. IP Adresse Physikalische Adresse :FF:AA:36:AB: :48:A4:28:AA:18

Blatt 2, Kommunikationskanäle. Betriebssysteme. Deadlocks. Kommunikationskanäle. y=12; recv(k2, y); y = y+4; send(k1, y);

Linux Prinzipien und Programmierung

Programmieren mit sockets

Client/Server-Systeme

U7-1 Hinweise zur Evaluation. U7-2 Netzwerkkommunikation und Byteorder. U7-3 Sockets. Evaluation. Frage "eigener Aufwand zur Vor- und Nachbereitung"

10. Vorlesung Betriebssysteme

Client/Server-Systeme

Themen. Transportschicht. Internet TCP/UDP. Stefan Szalowski Rechnernetze Transportschicht

1.) Nennen Sie Aufgaben und mögliche Dienste der Transportschicht (Transport Layer) des ISO/OSI-Schichtenmodells.

Netzwerkprogrammierung unter Linux und UNIX

Theoretische Aspekte

6.1 Verbindungsorientiert, Verbindunslos

Transmission Control Protocol (TCP)

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Labor Nachrichtentechnik, Versuch 11 Teil 2: Socket-Programmierung

Kapitel 4: Design von Client/Server-Software. Middleware in Java vieweg 2005 Steffen Heinzl, Markus Mathes

TCP-Sockets in Java und C. Verteilte Systeme Hochschule Regensburg Vorlesung 4, Universitätsstraße 31, Regensburg

Verbessertes Konzept: Monitore

Programmieren von UNIX-Netzen

Verteilte Systeme. Organisatorisches. Secure Identity Research Group

Speicherbasierte Kommunikation (T) Realisierung von Semaphoren (T) Shared Memory (P) Synchronisation mittels Semaphoren (P)

Beispiel für einen IPC-Server, der seinen Dienst über den Global Name Service im Netzwerk bekannt gibt. Header-Dateien einbinden

Einführung in die Systemprogrammierung unter Linux

Dienstspezifikation nach RFC

Kommunikation im Netzwerk

TCP/UDP. Transport Layer

Betriebssysteme Teil 11: Interprozess-Kommunikation

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Java und Netzwerkkommunikation

C - Grundlagen und Konzepte

Probeklausur zu Systemnahe Software II SS 2012 Dr. Andreas Borchert mit Markus Schnalke

Programmierung mit sockets

I. Interprozesskommunikation

Verteilte Systeme Kapitel 4: Realisierung von Netzwerkdiensten

Sockets - Eine Programmierschnittstelle für Kommunikation im Netz

Linux Prinzipien und Programmierung

Linux Prinzipien und Programmierung

Netzwerkprogrammierung mit C++

Programmierschnittstellen

shri Raw Sockets Prof. Dr. Ch. Reich

G.1 Überblick (2) G.1 Überblick (2) G.1 Überblick

Vorlesung "Verteilte Systeme" Wintersemester 2000/2001. Verteilte Systeme. Internet-Protokolle: FTP, TELNET, SMTP, DNS, NSP, NTP, HTTP,...

Eine Mini-Shell als Literate Program

IPv6 in der Praxis. Felix von Leitner CCC Berlin Dezember IPv6 in der Praxis

Alles Text Sockets à la Plan 9

Vorwort. Martin Gräfe. C und Linux. Die Möglichkeiten des Betriebssystems mit eigenen Programmen nutzen ISBN:

Verteilte Systeme - Java Networking (Sockets) 2 -

Client/Server Programmierung mit Sockets

Unterschiede zu Pipelines 165

Programmiertechnik. Teil 4. C++ Funktionen: Prototypen Overloading Parameter. C++ Funktionen: Eigenschaften

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN

Netzwerksicherheit Musterlösung Übungsblatt 5: Firewalls

MODBUS/TCP und Beckhoff Steuerelemente

Hintergrundinformationen/Begriffe. Netzwerkverbindungen. Virtuelle Kreise. Sockettypen. Inhalt, vorige Seite, nächste Seite

Einreihung in eine einfach verkettete Liste Elementaroperation zum Anhängen eines Kettenglieds

Leseprobe. Martin Gräfe. C und Linux. Die Möglichkeiten des Betriebssystems mit eigenen Programmen nutzen ISBN:

Praktikum Rechnernetze Aufgabe 3: Messung mit dem Protokollanalyzer

7.5 Maskerade. (spoofing)

UDP User Datagramm Protokoll

Übungspaket 29 Dynamische Speicherverwaltung: malloc() und free()

Vorlesung Betriebssysteme II

Betriebssysteme (BS) Interprozesskommunikation. Olaf Spinczyk.

Transkript:

PROGRAMMIEREN MIT UNIX/LINUX-SYSTEMAUFRUFEN UNIX/Linux-Interprozesskommunikation, zugehörige Systemaufrufe und Kommandos 12. UNIX/Linux-Sockets Wintersemester 2016/17

UNIX/Linux-IPC-Mechanismen Nachrichtenbasierter Informationsaustausch: 5. 1. Nachrichten(warte)schlangen ( message queues ) System V IPC 12. 2. Sockets BSD-UNIX Speicherbasierter Informationsaustausch: 6. 7. 8. 4. Gemeinsame Speicherbereiche( shared memory ) System V IPC 5. Pipes (Named Pipes und FIFOs) Version 7 Signale: 6. Asynchrone UNIX-Signale Version 7 Synchronisationsmechanismen: 11. 7. UNIX-Semaphore System V IPC ws2016/17 H.-A. Schindler Folie: 12-2

Nachrichtenbasierter Informationsaustausch Prozess 1 Prozess 2 Befehle Befehle Daten Daten Datentransport (Nachrichten) Expliziter Datentransport in Form von Botschaften (= messages) Aus einem Datenbereich wird in den anderen kopiert. Sendender Prozess: benutzt (Variante einer) Send-Funktion Empfangender Prozess: benutzt (Variante einer) Receive-Funktion ws2016/17 H.-A. Schindler Folie: 12-3

Sockets -Grundsätzliches Ursprung: erstmalig in BSD-UNIX 4.2 Bedeutung: sind rechnerübergreifend verwendbar (Internet!!), d.h. zur Kommunikation zwischen Prozessen auf verschiedenen (vernetzten) Rechnern (sog. verteilte Systeme) aber auch für IPC innerhalb nur eines Rechners damit universellster Kommunikationsmechanismus auch in anderen BS implementiert ws2016/17 H.-A. Schindler Folie: 12-4

Sockets zugehörige Systemaufrufe Umfang: Zu Sockets existiert das umfangreichste Sortiment an Systemaufrufen und Parametern. Da von UNIX-Systemen Sockets als (spezielle) Dateien angesehen werden, kann auch ein Teil der Funktionen für die Dateiarbeit verwendet werden. Beschränkung: wir beschränken uns zunächst auf die Funktionen: 1. socket(6) - Erzeugen eines Sockets 2. bind(6) - Anbinden einer zugehörigen Datenstruktur 3. connect() - Verbinden von 2 Sockets 4. send() - Senden einer Nachricht (bei verbundenen Sockets) 5. sendto(6) - wie send() auch ohne vorheriges connect() 6. recv() - Empfangen (bei verbundenen Sockets) 7. recvfrom() - Empfangen auch ohne vorheriges connect() ws2016/17 H.-A. Schindler Folie: 12-5

Zur Benutzung der Systemaufrufe Socket Prozess 1 (z.b. Client) Variante2a: ohne Verbinden: sendto(..) Erzeugen: socket(..) Anbinden: bind(..) Socket- Datenstruktur Variante1: Verbinden: connect(..) +send(..) +recv(..) Erzeugen: socket(..) bind(..) Prozess 2 (z.b. Server) Variante2b: ohne Verbinden: recvfrom(..) ws2016/17 H.-A. Schindler Folie: 12-6

Systemaufruf socket(..) erzeugt Socket und typisiert diesen Verwendung im Programm (Prinzip): int <id_sock; <id_sock = socket(<domain, <type, <protocol); Konkretes Beispiel: int id; id = socket(af_inet, SOCK_STREAM, 0); Parameter Symbol Typ Bedeutung Interpretation <id_sock int Rückkehrwert Identifikator des Sockets (auch: Socket-Handle) <domain int <type int <protocol int Zugehörigkeit zu bestimmter Gruppe Protokoll Adress-Familie oder Protokoll-Familie (20) AF_INET oder PF_INET: Internet-Socket AF_UNIX oder PF_UNIX: lokaler UNIX-Socket... Datenmodell + Verlässlichkeitseigenschaften SOCK_DGRAM:unzuverlässige, verbindungslose Kommunikation (normalerweise UDP) SOCK_STREAM:zuverlässige, verbindungsorient. Kommunikation (normalerweise TCP/IP) spezifisches Protokoll (meist System überlassen: 0) ws2016/17 H.-A. Schindler Folie: 12-7

verknüpft Socket-Datenstruktur mit erzeugtem Socket Systemaufruf bind(..) Verwendung im Programm (Prinzip): int <rb; <rb= bind(<id_sock, &<my_socketaddr, <socketaddress_length); Parameter Symbol Typ Bedeutung <rb int Rückkehrwert Konkretes Beispiel: int rw; rw=bind(id_socket1, &client_socketaddr, sizeof(client_socketaddr)); Interpretation Rückkehrwert des Systemaufrufs socket(..) <id_sock int Identifikator <my_ socketaddr <socketaddress _length &(struct sockaddr) int Adresse der Datenstruktur, die mit Socket verknüpft wird Länge dieser Datenstruktur ws2016/17 H.-A. Schindler Folie: 12-8

Datenstruktur sockaddr Für unterschiedliche Adressfamilien unterschiedliche Datenstrukturen verwenden: Adressfamilie AF_UNIX: #define UNIX_PATH_MAX 108 struct sockaddr_un{ sa_family_t sun_family; /* AF_UNIX */ char sun_path[unix_path_max] /* UNIX-Pfadname */ }; Adressfamilie AF_INET: struct sockaddr_in{ sa_family_t sin_family; /* AF_INET */ unsigned short sin_port; /* 16-Bit TCP- oder UDP-Port-Nr. */ struct in_addr sin_addr; /* 32-Bit IP-Adresse */ char sin_zero[8]; /* nur zum Auffüllen der Struktur */ }; /* einheitlich 16 Byte ( padding ) */ ws2016/17 H.-A. Schindler Folie: 12-9

Systemaufruf connect(..) Verbinden mit Socket des Kommunikationspartners (verbindungsorientierte Kommunikation) Verwendung im Programm (Prinzip): int <r; <r= connect(<id_sock, &<server_socketaddr, <socketaddr_length); Konkretes Beispiel: int rx; rx=connect(id_socket1, server_socketaddr, sizeof(struct sockaddr)); Parameter Symbol Typ Bedeutung Interpretation <r int <id_sock int Rückkehrwert Identifikator Rückkehrwert des Systemaufrufs socket(..) <server_ socket_addr &(struct sockaddr) Adresse der Datenstruktur des Sockets der Gegenstelle <socketaddr _length int Länge Länge der Datenstruktur ws2016/17 H.-A. Schindler Folie: 12-10

Systemaufruf send(..) Senden einer Botschaft (message) Verwendung im Programm (Prinzip): int <rs; <rs= send(<id_sock, &<message, <message_length, flags); Parameter Symbol <rs int Typ Bedeutung Rückkehrwert Konkretes Beispiel: int rw; rw = send(id_socket1, request, requestlength, 0); Interpretation <id_sock int Socket- Identifikator Rückkehrwert des Systemaufrufs socket(..) <message &char[ ] Anfangsadresse des Speicherbereichs im Programm, in welchem message spezifiziert <message _length int Länge Länge der message <flags zur Spezifikation spezieller Eigenschaften ws2016/17 H.-A. Schindler Folie: 12-11

Systemaufruf recv(..) Empfangen einer Botschaft (message) Verwendung im Programm (Prinzip): int <r; <r= recv(<id_sock, &<receive_buffer, <message_length, <flags); Konkretes Beispiel: int rw; rw = recv(id_socket1, buffer, bufferlength, 0); Parameter Symbol <rs int <id_sock int Typ Bedeutung Rückkehrwert Socket- Identifikator Interpretation Anzahl der tatsächlich empfangenen Byte Rückkehrwert des Systemaufrufs socket(..) <receive _buffer &char[ ] Anfangsadresse des Speicherbereichs im Programm, wo message zu deponieren <message _length int Länge Länge der message <flags zur Spezifikation spezieller Eigenschaften ws2016/17 H.-A. Schindler Folie: 12-12

Systemaufruf recvfrom(..) Empfangen einer Botschaft (message) und Abspeichern der Quelladresse Verwendung im Programm (Prinzip): int <r; <r = recvfrom(<id_sock, &<receive_buffer, <message_length, <flags, <source_address, <source_address_length); Konkretes Beispiel: int rw; rw = recvfrom(id_socket1, buffer, bufferlength, 0,...); Parameter Symbol <source address <source_ address_ length Typ struct sockaddr* socklen_t Bedeutung / Interpretation Wenn (<source_address NULL) wird Adresse der Quelle einer empfangenen Botschaft auf Paramter<source_address bereitgestellt, falls diese durch das verwendete Transportprotokoll zur Verfügung gestellt wird. Länge der Quell-Adresse Restliche Parameter: siehe recv()! ws2016/17 H.-A. Schindler Folie: 12-13

Systemaufrufe read( ) und write( ) Anstelle von send() kann auch write(),anstelle von recv() auch read() verwendet werden, beide aber ohne Parameter flags. Es gilt exakt die Syntax (= Aufruf im Programm ) von write()und read() für Arbeit mit Dateien. Verwendung im Programm (Prinzip): <x= read(<id_sock, &<receive_buffer, <message_length); <y= write(<id_sock, &<message, <message_length); Konkretes Beispiel: int u, v; u = read(id_socket1, buffer, bufferlength); v = write(id_socket2, &message, message_length); Parameter Symbol Typ Bedeutung <x, <y int Rückkehrwerte Interpretation Restliche Parameter: siehe send() bzw. recv()! ws2016/17 H.-A. Schindler Folie: 12-14

Beispielprogramm: server1.c #include <stdio.h #include <stdlib.h #include <sys/socket.h #include <sys/types.h #include <string.h #define socket_name_length 14 #define request_length 16 #define message_length 128 main() { int id_server_socket; // Socket-Handle (= Identifizierer) int rv_bind; // Rueckkehrwert von bind() int rv_connect; // Rueckkehrwert von connect() char server_socketname[socket_name_length] = /tmp/svsocket"; struct sockaddr server_socketaddr; char client_socketname[socket_name_length] = /tmp/clsocket"; struct sockaddr client_socketaddr; ws2016/17 H.-A. Schindler Folie: 12-15

N // zum Senden int rv_send; // Rueckkehrwert vom Senden char answer_buffer[message_length] = "Elbe"; // zum Empfangen int rv_receive; //Rueckkehrwert beim Empfangen char receive_buffer[request_length]; // Server-Socket erzeugen id_server_socket = socket(af_unix, SOCK_DGRAM, 0 ); N // Anbinden des Namens an Server-Socket server_socketaddress.sa_family = AF_UNIX; strcpy(server_socketaddress.sa_data, server_socket_name); rv_bind= bind(id_server_socket, &server_socket_address, sizeof(server_socket_address)); N ws2016/17 H.-A. Schindler Folie: 12-16

N // Warten auf Anfrage rv_receive=recv(id_server_socket, receive_buffer, request_length, 0); N // Nach einer Anfrage: Vorbereiten zum Antwort senden client_socketaddr.sa_family = AF_UNIX; strcpy(client_socketaddress.sa_data, client_socket_name); // Verbinden mit Server-Socket rv_connect= connect(id_server_socket, &client_socket_address, sizeof(struct sockaddr)); N // Senden rv_send = send( id_server_socket, answer_buffer, message_length,0); N // Sauber abschließen, Socket-Name entfernen unlink(server_socket_name); } ws2016/17 H.-A. Schindler Folie: 12-17

Sockets weitere Systemaufrufe weitere wichtige Aufrufe: listen(6) accept(6) zu Datenstruktur sockaddr für AF_INET Include-Dateien: #include <sys/socket.h #include <netinet/in.h ws2016/17 H.-A. Schindler Folie: 12-18

Datenstruktur sockaddr für AF_INET unterschiedliche Datenstrukturen für unterschiedliche Adressfamilien!! Adressfamilie AF_INET: struct sockaddr_in { sa_family_t sin_family; // AF_INET unsigned short sin_port; // 16-Bit TCP- oder UDP-Port-Nr. struct in_addr sin_addr; // 32-Bit IP-Adresse char sin_zero[8]; // nur zum Auffüllen der Struktur }; // einheitlich 16 Byte ( padding ) Beispiel: #define SERVER_PORT 4711 // Port-Nr. für Socket struct sockaddr_in ssock; // Einrichten der Datenstruktur ssock vom Typ sockaddr_in ssock.sin_family = AF_INET; // siehe oben ssock.sin_port = htons(server_port); // htons: konvertiert SERVER_PORT geeignet ssock.sin_addr.s_addr = INADDR_ANY; ws2016/17 H.-A. Schindler Folie: 12-19

Systemaufruf listen(..) legt Warteschlange für Server-Aufrufe an Verwendung im Programm (Prinzip): int <rl; <rl = listen(<id_sock, <number); Parameter Symbol Typ Bedeutung <rl int Rückkehrwert Konkretes Beispiel: int rw; rw = listen(id_socket1, 5); Interpretation <id_sock int <number int Beschreibung: Identifikator Rückkehrwert des Systemaufrufs socket(..) Anzahl gleichzeitig möglicher Anforderungen Über <number wird spezifiziert, wie viele Verbindungsanforderungen gleichzeitig akzeptiert werden. Dazu wird Warteschlange mit entsprechender Platzanzahl eingerichtet. Werden mehr als <number Verbindungen gleichzeitig gefordert, werden alle Anforderungen ab (<number + 1) zurückgewiesen ws2016/17 H.-A. Schindler Folie: 12-20

Stellt Empfangsbereitschaft eines Sever-Prozesses her Systemaufruf accept(..) Verwendung im Programm (Prinzip): int <r; <connect_id= accept(<id_serversocket, &<client_socket, &<clientsocket_length); Konkretes Beispiel: int xz; rw = accept(id_ssock, cl_socketaddr, &sizeof(struct sockaddr); Parameter Symbol Typ Bedeutung / Interpretation <connect_id int Rückkehrwert(Erfolgkontrolle und Verbindungd-ID) <id_ serversocket &<client _socket &<clientsocket _length int &(struct sockaddr) int Rückkehrwert des Systemaufrufs socket(..) Adresse der sockaddr-datenstruktur der Gegenstelle Länge einer Socket-Adresse accept() blockiert bis tatsächlich ein Aufruf stattfindet ws2016/17 H.-A. Schindler Folie: 12-21

Aufgabenstellung (Anregungen) 1. Analysieren Sie server1.c und client1.c. Lesen Sie dabei auch die Kommentare. Übersetzen Sie beide Programme und beobachten Sie deren Arbeit. (Server und Client dazu in jeweils eigenen Kommandofenstern starten! Reihenfolge beachten!) Die angelegten Sockets sind ähnlich wie benannte Pipes im Dateisystem sichtbar (bevor sie wieder gelöscht werden). Analysieren Sie z.b. nach dem Serverstart die entsprechenden Stellen im Dateisystem. Bei server2.c wurden recv() durch read() und send() durch write() ersetzt. 2. Übersetzen Sie server2.c und überprüfen Sie, ob auch dieser mit dem ungeänderten client1.c zusammenarbeiten kann. In server2b.c wurde als einzige Änderung gegenüber server1.c das Datenmodell des Sockets von SOCK_DGRAM auf SOCK_STREAM gesetzt. 3. Übersetzen Sie server2b.c und beobachten Sie! (Es reicht den Server zu starten.) ws2016/17 H.-A. Schindler Folie: 12-22

Auch server3.c ist eine Modifikation von server1.c. Es wurde eine einfache Schleife implementiert, so dass der Server wiederholt auf Anfragen antworten kann. (Für einen echten Server fehlt jedoch noch die Fähigkeit, die Anfragen zu analysieren und zur jeweiligen Anfrage passend zu antworten.) 4. Übersetzen Sie server3.c. Starten Sie verschiedene Clients, die Anfragen an den Server stellen und beobachten Sie. Ursprung aller Clients kann natürlich immer das Programm client1.c sein. ws2016/17 H.-A. Schindler Folie: 12-23

server4.c ist eine Modifikation von server3.c, wobei jetzt Internet-Sockets und eine Port-Nummer Anwendung finden. Anstelle von bind() und connect() werden jetzt listen() und accept() verwendet, die eine ähnliche Funktion haben. 5. Analysieren Sie server4.c. Übersetzen und starten Sie danach dieses Programm. Ein funktionsfähiges Programm muss blockieren, bis eine Anfrage kommt. Ob server4wirklich am Port 4711lauscht, kann durch das Kommando netstat a grep 4711 untersucht werden. Ein simpler Client kann dann mittels Kommando telnet<server_name 4711simuliert werden. (Wird das Kommando auf der gleichen Maschine eingegeben, auf der auch der Server läuft, ist telnetlocalhost4711 zu verwenden). Es werden dann die an der Tastatur eingegebenen Zeichen als Anfrage an server4übertragen. ws2016/17 H.-A. Schindler Folie: 12-24