Der verbindungslose Dienst wird am Beispiel eines Auftragssystems näher erläutert: Der Server wartet auf ankommende Aufträge, bearbeitet und beantwortet sie.
Lokale Verwaltung am Beispiel eines Auftragssystems
Wie beim verbindungsorientierten Dienst muss der Benutzer vor der Datenübertragung die lokale Verwaltung durchführen. Der Benutzer muss einen entsprechenden verbindungslosen Dienst mit t_open() aufrufen und seine Adresse mit t_bind() an den Transportendpunkt binden.
Der Benutzer kann die Funktion t_optmgmt() verwenden, um die Leistungsmerkmale des Protokolls zu ändern. Wie beim verbindungsorientierten Dienst hat jeder Transportanbieter seine eigenen Leistungsmerkmale. Deshalb macht die Verwendung von t_optmgmt() die Programme vom verwendeten Protokoll abhängig.
Mit den folgende Definitionen und Aufrufen führt der Server die lokale Verwaltung durch:
#include <stdio.h> #include <fcntl.h> #include <xti.h> #include <netinet/in.h> #include <sys/socket.h> #define SRV_ADDR 0x7F000001 #define SRV_PORT 8888 main() { int fd; nt flags; struct t_bind *bind; struct t_unitdata *ud; struct t_uderr *uderr; struct sockaddr_in *sin if ((fd = t_open("/dev/udp", O_RDWR, NULL)) < 0) { t_error("Der Transportanbieter kann nicht geöffnet werden"); exit(1); } if ((bind = (struct t_bind *)t_alloc(fd,T_BIND, T_ADDR)) == NULL) { t_error("t_alloc() der t_bind-Struktur gescheitert"); exit(2); } bind->addr.len=sizeof(struct sockaddr_in); sin=(struct sockaddr_in *)bind->addr.buf; sin->sin_family=AF_INET; sin->sin_port=htons(SRV_PORT); sin->sin_addr.s_addr=htonl(SRV_ADDR); bind->qlen = 0; if (t_bind(fd, bind, bind) < 0) { t_error("t_bind() gescheitert"); exit(3); }
Mit dem Aufruf von t_open() erzeugt der Server einen Transportendpunkt.
Mit dem Aufruf von t_bind() bindet der Server eine bestimmte Adresse an den Transportendpunkt, sodass mögliche Clients den Server erkennen und auf ihn zugreifen können. Mit t_alloc() legt der Server ein Objekt vom Datentyp t_bind an und versorgt in der Komponente addr des t_bind-Objekts die Komponenten buf und len mit den entsprechenden Werten. Die Adresse selbst wird dabei entsprechend der Adressstruktur der Internet-Kommunikationsdomäne strukturiert.
Ein entscheidender Unterschied zwischen verbindungsorientiertem und verbindungslosem Dienst besteht darin, dass der Inhalt der t_bind-Komponente qlen im verbindungslosen Dienst keine Bedeutung hat: Es können nämlich alle Benutzer Datagramme empfangen, sobald der Aufruf t_bind() beendet ist. Der Transportanbieter definiert beim verbindungsorientierten Dienst während des Verbindungsaufbaus eine Client/Server-Beziehung. Eine solche Beziehung existiert im verbindungslosen Modus nicht. Bei diesem Beispiel ist es nicht der Transportanbieter, der eine Client/Server-Beziehung definiert, sondern die Art der Anwendung.
Datenübertragung am Beispiel eines Auftragssystems
Sobald der Benutzer eine Adresse an den Transportendpunkt gebunden hat, kann er Datagramme senden und empfangen. Jede gesendete Nachricht wird von der Adresse des Empfängers begleitet.
Die nachstehende Folge von Aufrufen zeigt den Server in der Datenübertragungsphase:
if ((ud = (struct t_unitdata *)t_alloc(fd,T_UNITDATA, T_ALL)) == NULL) { t_error("t_alloc() der t_unitdata-Struktur gescheitert"); exit(5); } if ((uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL)) == NULL) { t_error("t_alloc() der t_uderr-Struktur gescheitert"); exit(6); } for(;;) { if (t_rcvudata(fd, ud, &flags) < 0) { if (t_errno == TLOOK) { /* * Fehler bei einem früher gesendeten Datagramm */ if (t_rcvuderr(fd, uderr) < 0) { t_error(„t_rcverr gescheitert“); exit(7); } fprintf(stderr, "fehlerhaftes Datagramm, error = %d \n", uderr->error); continue; } t_error("t_rcvudata() gescheitert"); exit(8); } /* * query() bearbeitet den Auftrag und schreibt die * Antwort in ud->udata.buf und die Länge in ud->udata.len */ query(ud); if (t_sndudata(fd, ud, 0) < 0) { t_error("t_sndudata() gescheitert"); exit(9); } } } query() { /* Aus Vereinfachungsgründen nur ein Abschnitt */ }
Um Datagramme speichern zu können, muss der Server zunächst mit t_alloc() ein Objekt vom Datentyp struct t_unitdata anlegen.
Die Struktur t_unitdata ist in <xti.h> wie folgt deklariert:
struct t_unitdata { struct netbuf addr; struct netbuf opt; struct netbuf udata; };
addr enthält für ankommende Datagramme die Absenderadresse. Für zu sendende Datagramme enthält addr die Empfängeradresse. opt gibt mögliche Optionen des verwendeten Protokolls an, die auf dieses Datagramm angewendet werden sollen. udata enthält die Benutzerdaten. addr, buf und udata müssen mit genügend großen Puffern versehen werden, um ankommende Datagramme zu speichern. Wie im Abschnitt "Verbindungsorientierter Dienst" beschrieben, gewährleistet dies die Angabe T_ALL beim Aufruf von t_alloc(). Die Komponente maxlen jeder Komponente (vom Typ struct netbuf) des erzeugten t_unitdata-Objekts wird von t_alloc() mit dem entsprechenden Wert versorgt.
Der Server legt auch ein Objekt vom Typ struct t_uderr an, um Datagrammfehler bearbeiten zu können (siehe unten).
Der Server läuft in einer Endlosschleife. Er empfängt Aufträge, bearbeitet diese und antwortet den Clients. Zuerst wird t_rcvudata() aufgerufen, um den nächsten Auftrag zu empfangen. t_rcvudata() empfängt das nächste mögliche Datagramm. Sollte kein Datagramm verfügbar sein, so blockiert t_rcvudata() den Prozess, bis ein Datagramm empfangen wird. Der zweite Parameter des Aufrufs t_rcvudata() spezifiziert das t_unitdata-Objekt, in dem das Datagramm abgespeichert werden soll.
Der dritte Parameter (flags) muss ein Zeiger auf einen Integer-Wert sein. Dieser Wert kann beim Beenden von t_rcvudata() auf T_MORE gesetzt sein, um anzuzeigen, dass der Puffer udata nicht groß genug war für die Aufnahme des gesamten Datagramms. In diesem Fall liefern weitere Aufrufe von t_rcvudata() den restlichen Teil des Datagramms.
Da der Puffer in diesem Beispiel mit t_alloc() angelegt wurde, kann dieser Fall nicht eintreten und der Server braucht flags nicht zu prüfen.
Bei erfolgreichem Empfang eines Datagramms ruft der Server query() auf, um den Auftrag zu bearbeiten.
Datagrammfehler
Wenn der Transportanbieter ein mit t_sndudata() übergebenes Datagramm nicht bearbeiten kann, wird dem Benutzer ein Fehler T_UDERR gemeldet. Bei diesem Fehler werden Adresse und Optionen des Datagramms sowie ein protokollabhängiger Fehlerwert zurückgeliefert. Die geschilderte Situation kann z.B. eintreten, wenn der Transportanbieter die angegebene Zieladresse nicht findet.
Von jedem Protokoll wird erwartet, dass es alle Ursachen angibt, warum ein Datagramm nicht gesendet wurde.
Die Fehleranzeige gibt keine Auskunft darüber, ob das Datagramm erfolgreich abgeschickt worden ist. Das Transportprotokoll entscheidet, wie die Fehleranzeige benutzt wird. Es sei hier nochmals betont, dass der verbindungslose Dienst keine zuverlässige Zustellung der Daten garantiert.
Der Server wird von dem Fehler unterrichtet, sobald er versucht, ein Datagramm zu empfangen. Der Aufruf t_rcvudata() scheitert, und t_errno wird auf TLOOK gesetzt. Wenn t_errno auf TLOOK gesetzt ist, kann nur ein T_UDERR aufgetreten sein, so dass der Server t_rcvuderr() aufruft, um die Fehlerursache festzustellen. Der zweite Parameter beim Aufruf t_rcvuderr() ist ein zuvor angelegtes Objekt vom Datentyp struct t_uderr. Dieses Objekt wird vom Aufruf t_rcvuderr() mit Werten versorgt.
Die Struktur t_uderr ist in <xti.h> wie folgt deklariert:
struct t_uderr { struct netbuf addr; struct netbuf opt; long error;};
addr und opt geben die Zieladresse und die für dieses Datagramm gesetzten Optionen an. error zeigt einen protokollabhängigen Fehlerwert an, der spezifiziert, warum das Datagramm nicht bearbeitet worden ist. Der Server gibt den Fehlerwert aus und kehrt dann in die normale Schleife zurück.