EINFÜHRUNG IN DIE SOCKETPROGRAMMIERUNG Was ist ein Socket? Also ein Socket ist ganz einfach gesagt ein Kommunikationsendpunkt. Er dient als Interface zwischen einem Netzwerkprotokoll, z.B. TCP/IP und einem Anwendungsprogramm, z.B. ftp oder telnet. Der Socket erleichtert dem Programmierer die Arbeit, da er sich nicht mehr um das Übertragungsprotokoll kümmern muß. Ursprünglich stammt der Socket aus Berkeley (BSD-Unix) und wurde später von Microsoft im Socket 1.1 übernommen. Wie funktioniert ein Socket? Ein Socket wird in einem C Programm durch die Funktion socket() erzeugt, die einzelnen Para- meter dieser Funktion werden später erklärt. Der Socket wird durch einen Integerwert beschrieben, dem sog. SocketDescriptor. Im Normalfall wird ein Socket von einer Gruppe von vier Nummern beschrieben. 1.) Die ID des entfernten Rechners, z.B. dessen IP 2.) Die PortNummer des entfernten Rechners 3.) Die ID des lokalen Rechners, z.B dessen IP 4.) Die Portnummer des lokalen Rechners Ein Socket kann durch mehrere Optionen konfiguriert werden. Er kann beispielsweise einen bestimmten Port belauschen und auf eine Verbindung warten. In diesem Fall ist der Socket dann Teil des Servers einer Client-Server Verbindung. Wie erzeuge ich einen Socket? Ein Socket wird durch folgende Funktion erzeugt. socket(int domain, int type, int protokoll); DOMAIN Dieser Parameter bezeichnet,zwischen welchen Objekten kommuniziert werden soll. Die am meisten verwendeten sind wohl AF_INET oder AF_UNIX. Diese beiden Typen erlauben es, das eine große Anzahl von Protokollen verwendet werden können. TYPE Beschreibt den Kommunikationstyp, einige davon können sein SOCK_STREAM. Stream basierte Kommunikation SOCK_DGRAM Datagram basierte Kommunikation SOCK_RAW hier werden RAW IP Sockets benutzt, dazu sind root Rechte notwendig PROTOKOLL wird im Normalfall auf 0 gesetzt. Der Socket wird mit dem SocketDescriptor beschrieben. Was kann ich mit einem Socket nun alles machen? Ein Socket ist nun in der Lage mehrere Funktionen auszuführen. Diese werden im folgenden näher beschrieben. bind() Der bind() Aufruf, weißt einem durch socket() erstellten Socket eine Adresse zu. Der Aufruf erfolgt so: bind(int socket, struct sockaddr *name, int groesse); SOCKET hier gehört der SocketDescriptor hin. Wurde der Socket vorher beispielsweise so erzeugt, main() { s = socket(.....); } dann gehört hier als erstes Argument s hinein. SOCKADDR *NAME Dieses Argument ist ein Pointer auf eine protokoll spezifische Adresse. GROESSE Beschreibt einfach die Größe der Adressstruktur name. connect() connect() dient zur errichtung einer Verbindung zwischen Client und Server. connect(socket, sockaddr *server, int groesse); Arg. 1 und 3 sind in der Erklärung zu bind() beschrieben. Das zweite Argument ist ein Pointer auf eine Adreßstruktur, die den Server beschreibt. Bei den meisten verbindungsorientierten Beziehungen(TCP/IP, SPX,...) dient der connect() Aufruf, dazu, dem RemoteRechner mitzuteilen, das eine Verbindung erwünscht ist. Dazu müssen die beiden Systeme oftmals bestimmte Parameter aufeinander abstimmen(z.B. die Puffergröße), ansonsten kann keine Verbindung zustandekommen. In diesem Fall gibt die Funktion eine Fehlermeldung zurück. Zu beachten ist, das im connect() Aufruf kein bestimmtes Protokoll angegeben werden muß, da das schon in der socket() Funktion geschehen ist. listen() In einer Client-Server Verbindung wird dieser Systemaufruf grundsätzlich vom Server verwendet. listen() signalisiert, das ein Server empfangsbereit ist. Aus der Source heraus wird er wie folgt aufgerufen. listen(socket, count); Argument 1 ist klar, count bezeichnet die Anzahl der Verbindungen die Maximal in die Warteschlange gestellt werden können. Der maximale Höchstwert ist zur Zeit 5. accept() Die funktion accept() nimmt sich die erste Verbindungsanfrage aus der Warteschlange und generiert einen neuen Socket mit den selben Eigenschaften wie dem in listen() spezifiierten Socket. Dieser neue Socket übernimmt dann die Anfrage und arbeitet sie ab. Wenn keine Anfragen mehr in der Warteschlange sind, blockiert accept(). Im Quellcode wird accept() wie folgt verwendet. accept(int socket, struct sockaddr *peer, int *groeße); Arg. 1 und 2 wurden weiter oben schon erklärt . Diese Struktur peer wird von accept() mit den Adressdaten des RemoteRechners gefüllt. accept() kann drei Werte zurückgeben. Es kann erstens ein neuer Socketdescriptor ausgegeben werden, zweitens die ProcessID des ChildProcess und die Größe der Adressstruktur. Wird eine Verbindung von accept() empfangen teilt der Systemaufruf fork() Den Prozess in einen Child und einen ParentProcess auf. Der ParentProcess wartet über listen() weiter auf neue Verbindungen, der ChildProcess bedient die durch accept() angenommene Verbindung. close() ------- Der close() Systemaufruf führt dazu, wie der Name schon sagt, das der Socket, den man mit socket() gebaut hat, geschlossen wird. Er wird aus der Source heraus ganz einfach so verwendet. close(socket); Der Kernel versucht, bevor er den Socket schließt, noch alle anstehenden Anfragen abzuarbeiten. All diese Operationen geben als Fehlermeldung den Wert -1 zurück. Daraus resultiert, das im Quellcode die Funktionen am besten so benutzt werden. main() { if(socket(s, sockaddr *addr, length) == -1) { printf("Error:SocketError"); } } Wie kann ich nun in diese Verbindung schreiben bzw Daten aus ihr lesen? Die durch die oben genannten Funktionen erstellte Verbindung kann nun dazu benutzt werden, Daten in die Verbindung zu schreiben, sowie Daten aus der Verbindung zu lesen. Dazu bedienen wir uns der System-Calls write() und read(). In der Source wird write() und read() mit den selben Argumenten gefüllt, der Aufruf geschieht wie folgt! write(int socket, sockaddr *daten, int groeße); In Argument 1 wird der zu benutzende Socket angegeben, Argument 2 enthält einen Zeiger auf die zu versendenden Daten, das Dritte Argument gibt die Anzahl der zu versendenden Bytes an. Der Aufruf von read() erfolgt gleich. Da TCP/IP ein Protokoll ist, das nicht garantiert, das alle Daten in einem Zug versandt werden, muß man, wenn Daten aus einer Verbindung gelesen werden, solange read() verwenden bis man sicher ist, das es keine zu empfangenen Daten mehr gibt. Read() muß also wiederholt aufgerufen werden, bis es keine zu empfangenen Daten mehr gibt. Wie komme ich an die Adressdaten des entfernten Rechners? --------------------------------------------------------- Für eine Verbindung sind grundsätzlich zwei Adressen nötig. Das ist einerseits die Rechneradresse, meistens eine IP Adresse, und zweitens die genaue PortNummer an die Daten gesendet werden sollen. DIE RECHNERADRESSE Zum Ermitteln der Rechneradresse stellt C eine Funktion namens gethostbyname() bereit. Dieser Funktion wird der Name des Rechners übergeben (z.B. www.ccc.de), und er ermittelt daraus die zugehörige IP Adresse. Der Aufruf erfolgt so: gethostbyname(Rechnername); In der Source schaut das dann ungefähr so aus main() { char remote struct hostent *hostinfo . . . if((hostinfo = gethostbyname(remote)) == NULL) { printf("Error: Cannot Resolve Host IP); exit(1); } } remote kann über argv[] deklariert werden, oder erst innerhalb des Programms durch Eingabe gesetzt werden. Mehr Informationen über die Funktion gethostbyname() stehen auf der Mainpage von gethostbyname. DIE PORTNUMMER Die zweite Information ist der Port, den der gewünschte Service belegt. Auch dafür gibt es wiederum eine Funktion, getservbyname(). getservbyname(SERVICE, "Protokoll"); Diese Funktion erwartet als Argumente den Namen des gewünschten Services sowie das Übertragungsprotokoll. Die ermittelten Informationen gibt es in eine Struktur zurück. Diese Funktion kann folgendermaßen verwirklicht werden! main() { struct servent *portinfo char service . . . if((portinfo = getservbyname(service, "tcp")) == NULL) { printf("Error:Cannot Resolve Service"); } } LAST WORDS Ich hoffe der Text stellt für einige von euch eine Einführung in die SocketProgrammierung dar. Falls ich Fehler gemacht habe oder ihr fragen habt, könnt ihr euch an XeroX99@hushmail.com wenden. XeroX 8.3.2k written for Oziris(http://www.Oziris.ForU.de) :: Information :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: Dieser Text hier stammt aus dem Tutorial Archiv des Login Club´s :: Visit: http://www.login-club.org | irc.euric.net 6667 #loginclub :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: :: ::