UNIX-Schnittstelle ================== 5. Systemrufe für globale Prozesskommunikation =============================================== - BSD spezifische Prozesskommunikation: Sockets - Remote Procedure Calls |
next | back | 2017 - 1 |
5.1 Kommunikation mit Sockets ============================= Sockets - Werkzeuge für die lokale und globale Kommunikation von Prozessen Vorgestellt: 1982 in BSD 4.1c für VAX Für die Kommunikation werden Protokolle benutzt: UNIX-Protokoll für die lokale Kommunikation Internet Protokolle TCP/IP und UDP/IP für globale/lokale Kommunikation IP-Adressen: global: Internetadresse 141.20.20.50 Klassen: A, B, C, D lokal: Portadresse+Protokoll (TCP - Transmission Control Protocol UDP - User Datagram Protocol IP - Internet Protocoll) Kommunikationspartner: Server Client mit festen Aufgaben bei der Kommunikation |
next | back | 2017 - 2 |
Verbindungsorientiertes Protokoll TCP/IP ---------------------------------------- Server Client socket() socket() | | V | bind() | | | V | listen() | | | V V accept() <----------- connect() | | V Daten V read() <------------- write() write() -------------> read() | Daten | V V close() close() |
next | back | 2017 - 3 |
Verbindungsloses Protokoll UDP/IP ------------------------------------ Server Client socket() socket() | | V V bind() bind() | | V Daten V recfrom() <------------ sendto() sendto() ------------> recfrom() | Daten | V V close() close() |
next | back | 2017 - 4 |
Socketadressen -------------- allgemeine Adresstruktur: struct sockaddr { u_short sa_family; /* address family: AF_INET, AF_UNIX, AF_NS, AF_IMPLINK */ char sa_data[14]; /*Adresse }; Hostadresse: struct in_addr { u_long s_addr; /* 32-bit host-id, network byte ordered */ }; Adresstruktur für Internetadressen: struct sockaddr_in { short sin_family; /* AF_INET */ u_short sin_port; /* 16-bit port number */ struct in_addr sin_addr; /* 32-bit host-id, network ordered */ char sin_zero[8]; /* unused */ }; Headerfiles: #include <sys/types.h> #include <sys/socket.h> |
next | back | 2017 - 5 |
int socket(int family, int type, int protocol); Erzeugen eines Kommunikationsendpunktes (socket). Es wird die Adressfamilie family, der Protokolltyp type und das Protokoll protocol für den Socket festgelegt. family: AF_UNIX - interne UNIX-Adressen (Filenamen) AF_INET - Internetadressen AF_NS - Xerox NS Adressen AF_IMPLINK - IMP Adressen (ARPANET) type: SOCK_STREAM - stream socket (verbindungsorientiert) SOCK_DGRAM - datagram socket (verbindungslos) SOCK_RAW - raw socket (Raw-Devices) protocol: IPPROTO_UDP - UDP IPPROTO_TCP - TCP IPPROTO_ICMP - ICMP IPPROTO_RAW - raw 0 - das zu type passende Protokoll wird ausgewählt Gültige Kombinationen von type und protocol: AF_UNIX AF_INET SOCK_STREAM ja TCP SOCK_DGRAM ja UDP SOCK_RAW IP/ICMP |
next | back | 2017 - 6 |
socket() gibt einen Socket-ID (sockfd) zurück, der eine Datenstruktur repräsentiert. Damit diese Datenstuktur für eine Kommunikation genutzt werden kann, mussen folgende Informationen in ihr eingetragen sein: Protokoll: von socket() lokale Adresse: von bind() lokaler Port: von bind() remote Adresse: von accept(), connect() remote Port: von accept(), connect() Rückkehrwert: >=0 - Socket-ID <0 - Fehler EAFNOSUPPORT - Adresse nicht von Kern unterstützt EPROTONOSUPPORT - Sockets unterstützt nicht die angegbene Adressfamilie EMFILE - kein Deskritor frei ENOBUFS - keine Puffer frei ENOMEM - kein Kernspeicher frei EPERM - ein nicht SU versucht einen RAW-socket zu eröffnen. |
next | back | 2017 - 7 |
#include <sys/types.h> #include <sys/socket.h> int socketpair(int family, int type, int protocol, int sockvec[]); Erzeugen zweier miteinander verbundener Kommunikationsendpunkte (Sockets) sockvec[0] und sockvec[1] ähnlich einer Pipe, allerdings sind diese Sockets bidirektional im Gegensatz zur Pipe, die uni- direktional ist. family, type und protocol haben die gleiche Bedeutung wie bei socket(). Rückkehrwert: = 0 - OK < 0 - Fehler wie bei socket() EFAULT - sockvec[] hat unzulässige Adresse |
next | back | 2017 - 8 |
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *myaddr, int addrlen); bind() dient zum Binden der Adresse (Hostadresse, Port) in *myaddr an einen ungebundenen Socket sockfd. Server müssen bind() ver- wenden. Dies ist notwendig damit ein Client mit Hilfe dieser Adresse einen Server erreichen kann. Clienten müssen bind() bei der Benutzung verbindungsloser Protokolle verwenden. addrlen gibt die Länge von *myaddr an. Rückkehrwert: =0 - Ok <0 - Fehler EBADF - sockfd ist falsch ENOTSOCK - sockfd ist ein File und kein Socket EADDRNOTAVAIL - falsche Hostadresse EADDRINUSE - Port wird bereits benutzt EINVAL - Socket sockfd ist bereits gebunden EACCES - kein Zugriff auf diesen Port (<1024) EFAULT - falsche Adresse von myaddr |
next | back | 2017 - 9 |
#include <sys/socket.h> int listen(int sockfd, int backlog); Mit listen() teilt ein verbindungsorientiert arbeitender Server dem Kern mit, wieviele ausstehende Verbindungsanforderungen (backlog) für den Socket sockfd in einer Queue gespeichert werden sollen. Maximum wird durch SOMAXCONN (5..8) festgelegt. Rückkehrwert: =0 - Ok <0 - Fehler EBADF - sockfd ist falsch ENOTSOCK - sockfd ist ein File und kein Socket EOPNOTSUPP - Sockettyp untestützt listen nicht. |
next | back | 2017 - 10 |
#include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *peer, int *addrlen); accept() nimmt eine neue Verbindungsanforderung für den gebundenen Socket sockfd an und erzeugt für diese Verbindung einen neuen Socket. sockfd ist ein Socket, der zuvor mit bind() und listen() gebunden wurde. peer zeigt auf ein Feld mit der Länge *addrlen auf dem die Adresse des Clienten nach Ausführung des Rufes accept() abgelegt wird. *addrlen enthält nach accept() die tatsächliche Länge der Adresse des Partners. Rückkehrwert: >=0 - Socket-ID der neuen Verbindung <0 - Fehler EINVAL,EOPNOTSUPP - Für sockfd accept nicht zugelassen EBADF - sockfd ist falsch ENOMEM - kein Kernspeicher frei ENOTSOCK - sockfd ist ein File und kein Socket EFAULT - falsche Adresse von peer EMFILE - kein Deskritor frei EWOULDBLOCK - es liegt keine Verbindungsanforderung vor (Socket ist nichtblockierend) |
next | back | 2017 - 11 |
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *servaddr, int addrlen); connect() stellt eine Verbindung zwischen zwei Sockets her. Beide Sockets müssen das gleiche Protokoll und die gleiche Adressfamilie benutzen. sockfd spezifiziert den lokalen Socket und *servaddr spezifiziert die Adresse des remote Socket. addrlen gibt die Länge der Adresse an. Durch connect() wird beim Clienten ein freier Port gebunden, wenn dies durch bind() noch nicht erfolgte. SOCK_DGRAM-Socket: connect() möglich, send() und recv() benutzbar kein verbindungsorientiertes Protokoll!!! SOCK_STREAM-Socket: verbindungsorientiertes Protokoll wird benutzt, read(), write() möglich Rückkehrwert(connect()): =0 - connect() erfolgreich <0 - Fehler EBADF - sockfd ist falsch ENOTSOCK - sockfd ist ein File und kein Socket EADDRNOTAVAIL - falsche Hostadresse EAFNOSUPPORT - Adresse nicht von Kern unterstützt EISCONN - Socket schon verbunden ETIMEOUT - Timeout aufgetreten ECONNREFUSED - connect() zurückgewiesen EADDRINUSE - Adresse benutzt EFAULT - falsche Adresse von servaddr EWOULDBLOCK - es liegt kein accept() beim Server vor (Socket ist nichtblockierend) |
next | back | 2017 - 12 |
Beispiele: TCP-Sockets Talk mit Warten auf den Partner inet.h s_sock.c c_sock.c mit IP-Adresse c_sock0.c mit Hostnamen |
next | back | 2017 - 13 |
#include <sys/types.h> #include <sys/socket.h> int send(int sockfd, char *buff, int nbytes, int flags); int recv(int sockfd, char *buff, int nbytes, int flags); int sendto(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *to, int addrlen); int recfrom(int sockfd, char *buff, int nbytes, int flags, struct sockaddr *from, int *addrlen); int sendmsg(int sockfd, struct msghdr *buff, int flags) int recvmsg(int sockfd, struct msghdr *buff, int flags) send(), sendto() und sendmsg() werden zum Senden von Daten mittels verbindungsloser oder verbindungsorientierter Protokolle benutzt. recv(), recfrom() und recvmsg() werden zum Empfangen von Daten mittels verbindungsloser oder verbindungsorientierter Protokolle benutzt. Wird send() bzw. recv() für verbindungslose Protokolle benutzt, so ist vorher ein connect() notwendig. sockfd - für die Kommunikation benutzter Socket to, from - dienen der Adresspezifikation für verbindungslose Protokolle addrlen - Länge der Adresse |
next | back | 2017 - 14 |
flags - spezielle zusätzliche Möglichkeiten beim Senden und Empfangen: MSG_OOB - senden und empfangen von out-of-band Daten MSG_PEEK - besichtigen der Daten ohne entfernen aus dem Datenstrom (recv, recvfrom) MSG_DONTROUTE - Routingtabelle nicht benutzen (nur sendmsg) nbytes - Länge des Datenpuffers buff - Adresse des Datenpuffers Bei recvmsg() und sendmsg() hat der Datenpuffer folgende Struktur: struct msghdr { caddr_t msg_name; /* optional address */ int msg_namelen; /* size of address */ struct iovec *msg_iov; /* address io-vector */ u_int msg_iovlen; /* # elements in msg_iov */ caddr_t msg_control; /* address of control-data-buffer */ int msg_controllen; /* length of control-data */ int msg_flags; /* flags for send/recv */ }; Rückkehrwert: >=0 - Anzahl der übertragenen Bytes <0 - Fehler EBADF - sockfd ist falsch ENOTSOCK - sockfd verweist auf ein File EWOULDBLOCK - es liegt kein accept() beim Server vor (Socket ist nichtblockierend) EINTR - Signal aufgetreten vor Ende des Calls EFAULT - buff-Parameter falsch EMSGSIZE - zu grosse Länge (nbytes) |
next | back | 2017 - 15 |
#include <sys/types.h> #include <sys/socket.h> getsockopt(int sockfd, int level, int option_name, char *option_value, int *option_len) setsockopt(int sockfd, int level, int option_name, char *option_value, int option_len) Lesen (getsockopt()) bzw. setzen (setsockopt()) von Optionen für den Socket sockfd. level gibt das Protokoll an. option_name spezifiziert die gewünscht Option. Folgende Optionen sind möglich: SO_DEBUG - Debug-Flag (int) SO_ACCEPTION - Listen enable (int) SO_BROADCAST - Broadcast supported (int) SO_KEEPALIVE - Connection aktiv (int) (SIGPIPE) SO_DONTROUTE - Benutzung der Standardrouten (int) SO_USELOOPBACK - Sender erhält Kopie der gesendeten Daten (int) SO_LINGER - warten auf Übertragungsende bei close() (int) SO_OBINLINE - out-of-band Data (int) SO_SNDBUF - Puffergrösse send (int) SO_RCVBUF - Puffergrösse recv (int) SO_SNDTIMEO - Sende-Time-Out (struct timeval) SO_RCVTIMEO - Empfangs-Time-Out (struct timeval) nur für getsockopt() SO_ERROR - Errorstatus (int) SO_TYPE - Sockettype option_value spezifiziert einen Puffer für den Wert der entsprechenden Option und option_len die Länge des Puffers. |
next | back | 2017 - 16 |
Rückkehrwert: >=0 - Ok <0 - Fehler EBADF - sockfd ist falsch ENOTSOCK - sockfd verweist auf ein File EFAULT - option_value- oder option_len-Parameter falsch ENOPROTOPT - Option unbekannt |
next | back | 2017 - 17 |
#include <sys/socket.h> int getsockname(int sockfd, struct sockaddr *address, int *adr_len) int getpeername(int sockfd, struct sockaddr *address, int *adr_len) Holen der zu einem Socket gehörenden Adressen: getsockname() - lokale Adresse getpeername() - remote Adresse sockfd - Socket für den die Adresse bestimmt werden soll *address - Puffer für die lokale/remote Adresse *addrlen - Länge des Adressfeldes/der Adresse Rückkehrwert: >=0 - Ok <0 - Fehler EBADF - sockfd nicht zulässig ENOTSOCK - kein Socket, sondern File ENOBUFS - keine Puffer vorhanden EFAULT - address oder addrlen falsch |
next | back | 2017 - 18 |
Bibliotheks-Hilfsroutinen für Netzwerkdienste ---------------------------------------------- Byteorder-Routinen: ------------------- #include <sys/types.h> #include <netinet/in.h> #include <inttypes.h> uint32_t htonl(unint32_t hostlong); Konvertieren einer long-Integer von der Host-Darstellung in die Netzdarstellung. uint16_t htons(uint16_t hostshort); Konvertieren einunsigned-short-Integer von der Host- Darstellung in die Netzdarstellung. uint32_t ntohl(uint32_t netlong); Konvertieren einer long-Integer von der Netz-Darstellung in die Host-Darstellung uint16_t ntohs(uint16_t netshort); Konvertieren einer unsigned-short-Integer von der Netz- Darstellung in die Host-Darstellung |
next | back | 2017 - 19 |
Routinen für die Adressumrechnung ---------------------------------- #include <netdb.h> struct hostent *gethostbyname(const char *name); struct hostent *gethostbyname_r(const char *name, struct hostent *result, char *buffer, intbuflen, int *h_errnop); Bestimmen der Host-Informationen mittels Hostnamen. Adressen in Netz-Darstellung. struct hostent *gethostbyaddr(const char *addr, int len, int type); struct hostent *gethostbyaddr_r(const char *addr, int length, int type, struct hostent *result, char *buffer, int buflen, int *h_errnop); Bestimmen der Host-Informationen mittels Host-Adresse. Adresse in Netz-Darstellung. struct hostent { char *h_name; /* canonical name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses */ }; |
next | back | 2017 - 20 |
Routinen für Adressmanipulationen ------------------------------------ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_addr(const char *cp); a.b.c.d ---> Hostadresse long int unsigned long inet_network(const char *cp); a.b.c ---> Netzwerkadresse long int struct in_addr inet_makeaddr(const int net, const int lna); Netzwerkadresse+lokale Hostadresse in in_addr-Struktur int inet_lnaof(const struct in_addr in); in_addr-Struktur --> lokale Hostadresse int inet_netof(const struct in_addr in); in_addr-Struktur --> Netzwerkadresse char *inet_ntoa(const struct in_addr in); in_addr-Struktur --> a.b.c.d |
next | back | 2017 - 21 |
Beispiele: UDP-Sockets - Talk mit Warten auf den Partner su_sock.c cu_sock.c Talk ohne Warten auf den Partner (Prozesse) s_sock1.c c_sock1.c s_sock1r.c mit Client-Adresse bestimmen Talk ohne Warten auf den Partner (Threads) c_sock_t.c s_sock1_t.c s_sock2_t.c Hilfsprogramme addr1 - IP-Adresse -> Hostname addr1 141.20.20.50 addr2 - Hostname -> IP-Adresse addr2 star msb - Most significant byte msb 141.20.21.50 |
next | back | 2017 - 22 |
Messungen, Zuverlässigkeit TCP: tcptd.c - Server Paketecho auf star, nbellus, garak: tcptd tcpt.c - Test Paketecho tcpt hostname Paketlaenge Paketanzahl auf Notebook, amsel: time tcpt star 512 100 UDP: udptd.c - Server Paketecho auf star, nbellus, garak: udptd udpt.c - Test Paketecho udpt hostname Paketlaenge Paketanzahl auf Notebook, amsel: time udpt star 512 100 |
next | back | 2017 - 23 |
5.2 Remote Procedure Call ========================= Modell für Remote Procedure Call (RPC) +-------+ +-------+ |Client-| |Server-| |routine| |routine| +-------+ +-------+ 1 | ^ 10 6 | ^ 5 V | v | +-------+ +-------+ |Client-| |Server-| | stub | | stub | +-------+ +-------+ 2 | ^ 9 7 | ^ 4 V | v | +-------+ 8 +-------+ | Netz- |<-----------------| Netz- | |routine|----------------->|routine| +-------+ 3 +-------+ 1 - lokaler Prozeduraufruf 6 - Übergabe des Prozedurergebnis 2 - Netzaufruf 7 - Netzaufruf 3 - Nachrichtenübertragung 8 - Nachrichtenübertragung 4 - Server wartet 9 - Clientstub wartet auf Ergebnis 5 - Aufruf der Serverprozedur 10 - Rückkehr zur Clientfunktion |
next | back | 2017 - 24 |
1 - Der Client ruft eine lokale Prozedur im Client-Stub auf (die gewünschte Prozedure). Der Client-Stub verpackt die Parameter. 2 - Der Client-Stub übergibt die Netznachricht an den Kernel (socket) zum Transport. 3 - Der Kernel überträgt die Netznachricht an das entfernte System. 4 - Der Server-Stub wartet auf das Eintreffen von Netznachrichten. Diese werden ausgepackt. 5 - Der Server-Stub ruft die eigentliche Prozedure auf. 6 - Die Prozedur wird ausgeführt und gibt die Ergebnisse an den Server-Stub. 7 - Der Server-Stub packt die Ergebnisse ein und übergibt die Netznach- richt an den Kernel. 8 - Der Kernel überträgt die Netznachricht an das rufende System. 9 - Der Client-Stub wartet auf das Ergebnis und packt es aus. 10- Der Client-Stub übergibt das Ergebnis an den Clienten. |
next | back | 2017 - 25 |
SUN-RPC ------- SUN-RPC ermöglicht die Benutzung von mehreren Remote-Prozeduren pro Programm. Die Prozeduren müssen vorher definiert werden. Das Programm muß sich beim Portmapper registrieren. Parameterübergabe Die Parameterübergabe ist nur für value-Parameter erlaubt. Hierbei transformiert der Client-Stub die Parameter in Netzformat. call-by-reference Parameter sind nicht zulässig, da der Server-Stub keine Informationen darüber hat, was sich hinter der Adresse verbirgt. Es ist jeweils nur ein Parameter und nur ein Ergebnis erlaubt. Sollen komplexere Werte ausgetauscht werden, müssen Strukturen benutzt werden. Bindung Mittels des PortMapper-Daemons (rpcbind, portmap) kann ein Client Verbindung zu einem Pogramm auf einem entfernten System knüpfen. Dazu ist es notwendig, daß auf dem Server der Server-Stub sich beim Portmapper registriert hat. Der Client erhält vom Portmapper dann den aktuellen Port für das gewünschte Programm. Die Identifzierung erfolgt über Programmnummer und Versionsnummer. |
next | back | 2017 - 26 |
entferntes System (Server) ........................................................... . +-------------+ 1 +----------+ . . | Portmapper |<-----------------------| Server- | . . | Daemon | | programm | . . +-------------+ +----------+ . ........^....................................^..^.......... | | | |2 3| |4 | ............................ | | | . +-----------+ . | | +------------->| Client- |<--------+ | . | program |<-----------+ . +-----------+ . ............................ lokales System 1 - Beim Start des Servers. Der Server erzeugt einen Socket. mittels der Funktion svc_register registriert der Server das Programm (Nummer) und die Version unter dem Port beim Portmapper 2 - Der Client fragt den Portmapper mittels Programmnummer und Programmversion nach dem Port für das Programm. 3 - Der Client sendet den Prozedurcall 1 4 - Der Client sendet den Prozedurcall 2 |
next | back | 2017 - 27 |
Transportprotokoll UDP (maximale Paketlänge 8192 Bytes), TCP (keine Grenze) Ausnahmebehandlung Lokale Ausnahme in einer Prozedur sind immer klar erkennbar (Division durch Null, Speicherschutzverletzungen, ...). Bei RPC kommen zusätzlich Fehler hinzu: Netzwerkfehler, Lastprobleme, Absturz des Clienten, Absturz des Servers. UDP: wenn keine Antwort kommt wird der Call wiederholt. Feste Anzahl von Wiederholungen, dann Rückgabe eines Fehlerkodes. TCP: Absicherung durch TCP. Rückgabe eines Fehlerkodes. Semantik der Aufrufe Bei einer lokalen Prozedur ist klar, wie oft sie abgearbeitet wurde, nachdem sie aufgerufen wurde - genau ein Mal. Bei einer Remote-Prozedure ist das nicht eindeutig. Folgend Situationen sind möglich: nicht aufgerufen - der Call ist beim Server nicht angekommen genau einmal - alles ok mehrmals - bei der Datenübertragung ist etwas schief gegangen. Bei SUN-RPC wird jedem Call eine eindeutige zufällige Durchführungs-ID zugeordnet. Diese wird bei Rückkehr auf Gleichheit geprüft. Dadurch ist gesichtert, daß das Ergebnis zu dem Call gehört. Duplikate werden vom Server- bzw. Client-Stub aussortiert. |
next | back | 2017 - 28 |
Datenrepräsentation Besonders problematisch ist die Datenübertragung zwischen verschieden Architekturen. Deshalb muß bei der Datenübertragung klar sein, was für Daten übertragen werden und wie sie dargestellt worden sind. Dafür gibt es XDR (eXternal Data Representation). Die entsprechenden Konver- tierungsroutinen werden durch das Programm rpcgen automatisch erzeugt. Sicherheit SUN-RPC unterstützt folgende Authentifzierungsmöglichkeiten: Null-Authentifikation - keine Authentifizierung UNIX-Authentifikation - bei jedem RPC-Call werden folgende Information mitgeliefert: Zeitstempel, Host, UID, GID DES-Authentifikation - DES-Verfahren. |
next | back | 2017 - 29 |
Wichtige Library-Calls für SUN-RPC ---------------------------------- CLIENT *clnt_create(char *host, unsigned long prog, unsigned long vers, char *proto); Erzeugen einer Verbindung zu einem Programm auf einem Server. host - Servername prog - Programmnummer vers - Programmversion proto - Protokoll ("tcp","udp") ------------------------------------------------------ clnt_destroy(CLIENT *clnt); Löschen einer Verbindung |
next | back | 2017 - 30 |
void clnt_pcreateerror(char *s); Verbindungsfehler ausgeben, CLIENT-Handle nicht erzeugt ------------------------------------------------------ void clnt_perrno(enum clnt_stat stat); Standardfehlertext ausgeben (callrpc()) ------------------------------------------------------ clnt_perror(CLIENT *clnt, char *s); Fehler nach Prozeduraufruf(clnt_call()) |
next | back | 2017 - 31 |
Erzeugen eines RPC-Programms (Schema für Beispiel) Server-Prozedur Server-Programm +-----------+ +--------+ |dated.c |---------------+---------->gcc---->|dated | +-----------+ ^ ^ +--------+ | | | +---+ | | R | | | P | +-----------+ | C | RPC-Spezifikation |date_svc.c | | - | +------+ +-----------+ | L | |date.x|--->rpcgen--> |date.h | | I | +------+ +-----------+ | B | |date_clnt.c| +---+ +-----------+ | | | | | +-----------+ V V +--------+ | rdate.c |---------------+---------->gcc---->| rdate | +-----------+ +--------+ Client-main-Funktion Client-Programm |
next | back | 2017 - 32 |
Spezifikation für rpcgen, daraus wird date_svc.c date.h und date_clnt.c date.x /* date.x - Specification von "remote date" and "time service" */ /* * Definition von zwei Prozeduren: * bin_date_1() Rueckgabe time and date als long integer. * str_date_1() Rueckgabe von time und date in lesbarer Form */ program DATE_PROG { version DATE_VERS { long bin_date(void) = 1; /* Prozedurenummer = 1, bin_date_1 */ string STR_DATE(long) = 2; /* Prozedurenummer = 2, str_date_1 */ } = 1; /* Versionsnumber = 1 */ } = 1234567; /* Programmnummer = 1234567 */ |
next | back | 2017 - 33 |
Server-Programm, nur die Prozeduren sind zu definieren, alles Andere kommt von rpcgen dated.c /* dated.c - Remote Prozedure; aufgerufen durch Server-Stub */ #include <time.h> #include "date.h" /* von rpcgen, enthaelt #include <rpc/rpc.h> */ /* Rueckgabe der Zeit in Sekunden */ long * bin_date_1() { static long timeval; /* muss static sein */ timeval = time((long *) 0); return(&timeval); } /* Datum und Uhrzeit menschlich lesbar */ char ** str_date_1(long *bintime) { static char *ptr; /* muss static sein*/ ptr = ctime(bintime); /* uebersetzen in local time */ return(&ptr); /* Rueckgabe der Adresse des Zeigers */ } |
next | back | 2017 - 34 |
Client-Programm: rdate.c /* rdate.c - Client Program fuer "remote date service" */ #include <stdio.h> #include "date.h" /* erzeugt durch rpcgen */ int main(int argc, char *argv[]) { CLIENT *cl; /* RPC handle */ char *server; long *lresult; /* Rueckkehrwert von bin_date_1() */ char **sresult; /* Rueckkehrwert von str_date_1() */ if (argc != 2) { fprintf(stderr, "usage: %s hostname\n", argv[0]); exit(1); } server = argv[1]; /* Erzeuge Client "handle" */ if ( (cl = clnt_create(server, DATE_PROG, DATE_VERS, "udp")) == NULL) { /* Verbindung zum Server konnte nicht hergestellt werden */ clnt_pcreateerror(server); exit(2); } |
next | back | 2017 - 35 |
/* Aufruf von "BIN_DATE" */ if ( (lresult = bin_date_1(NULL, cl)) == NULL) { clnt_perror(cl, server); exit(3); } printf("time on host %s = %ld\n", server, *lresult); /* Aufruf von "STR_DATE" */ if ( (sresult = str_date_1(lresult, cl)) == NULL) { clnt_perror(cl, server); exit(4); } printf("time on host %s = %s", server, *sresult); clnt_destroy(cl); /* close Client handle */ exit(0); } Aktionen zur Bildung: rpcgen -k date.x gcc -c -o date_proc.o date_proc.c gcc -c -o date_svc.o date_svc.c gcc -o date_svc date_proc.o date_svc.o gcc -c -o rdate.o rdate.c gcc -c -o date_clnt.o date_clnt.c gcc -o rdate rdate.o date_clnt.o |
back | 2017 - 36 |