Wie sieht's aus?
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Was tut's?
Mit socket() wird ein neuer Socket angelegt. Der Rückgabewert stellt einen Filedeskriptor dar, den man dann für die anderen Socket-Funktionen einsetzen kann. Intern wird ein Socket sogar gar nicht unähnlich zu einer geöffneten Datei verwaltet - das schließt ein, daß er beim Beenden des Programms automatisch geschlossen wird, aber dennoch (guter Stil) vorher explizit mit close() geschlossen werden sollte. Dies spart Ressourcen, und kann auch verwendet werden, um den Puffer zu flushen. In diesem Zusammenhang ist auch shutdown() interessant.
Es gibt eine Socket-Option namens SO_LINGER, die das Verhalten beim Aufruf von close() beeinflusst. Normalerweise blockiert close() nicht, noch ausstehende Daten werden vom System versendet. Allerdings ist ungewiß, ob dies auch klappt, denn nach dem close() hat man keine Möglichkeit mehr dies zu prüfen. Durch SO_LINGER kann eine Wartezeit angegeben werden. Außerdem kann bewirkt werden, daß ein Socket sofort geschlossen wird, ohne daß die noch ausstehenden Daten versendet werden. Eine ausführlichere Beschreibung findet sich in meinem Socket-Buch (siehe Startseite), Kapitel 8.1.5.
Unter Windows gibt diese Funktion INVALD_SOCKET zurück, wenn sie fehlschlägt, unter UNIX gibt's -1.
Was machen die Parameter?
Der erste Parameter, domain, gibt den Gültigkeitsbereich des Sockets an. Die Familien sind in sys/socket.h definiert, und umfassen (unter anderem) PF_UNIX und PF_INET. Ersteres ist auch unter PF_LOCAL bekannt. Zu allen PF_ Konstanten existieren auch AF_-Varianten. AF steht für Address Family, PF für Protocol Family. Semantisch korrekt wären hier also die PF-Konstanten, aber da sie seit jeher von Programmierern durcheinandergeworfen werden, darf man getrost davon ausgehen, daß sie identisch sind (wenn man nicht gerade an das System eines POSIX-Fetischisten geraten ist, der knallhart nach den Regeln des Standards spielt und darauf besteht, daß ihm die Leute ins Messer laufen).
Wir wollen uns in diesem Tutorial nur um Netzwerk-Programmierung mit TCP/IP beschäftigen, weshalb für uns PF_INET, welches die ARPA Internet Protocols bezeichnet, von besonderem Interesse ist.
Der zweite Parameter, type, gibt den Typ des Sockets an. Ich möchte hier nur auf SOCK_STREAM und SOCK_DGRAM eingehen, denn die anderen sind recht plattformspezifisch.
SOCK_STREAM gibt an, daß man einen Datenstrom übertragen will, und soweit nicht anders angegeben wird hier TCP zum Einsatz kommen. TCP garantiert ja bekannterweise, daß die Daten genau in der Reihenfolge (byteweise gesehen!) auf der anderen Seite ankommen, wie sie losgesandt wurden. Nicht notwendigerweise in den gleichen Brocken!
Mit SOCK_DGRAM kündigt man den Wunsch nach Datagrammen an, hier kommt klassischerweise UDP zum Einsatz. Bei UDP ist die Reihenfolge nicht garantiert, nichtmal daß alles ankommt. Dafür ist es sehr viel schlanker und für Anwendungen die ihre Kontrolle selbst implementieren wollen, oder aber auf Verlust nicht kritisch reagieren (Videostreams beispielsweise), bestens geeignet. Weiterhin sind forschrittliche Socketverwendungen wie Broadcast und Multicast auf UDP beschränkt.
Der dritte Parameter, protocol, gibt den genauen Wunsch nach einem bestimmten Protokoll an. Wir werden hier immer 0 angeben, womit der vernünftige Standard verwendet wird (also TCP bei SOCK_STREAM und UDP bei SOCK_DGRAM). Abweichende Protokolle können durch die Angabe von IPPROTO_xxx eingestellt werden, wobei nicht alle Kombinationen erlaubt sind.
Wie verwende ich es?
int s;
s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1)
{
perror("socket() failed");
return 1;
}