rshd
, rlogind
,
timed
, mountd
, nfsd
, snmpd
, inetd
, ...
select()
pour attendre
les nouveaux clients, si leur nombre est inconnu à l'avance
Bob Scheifler, Jim Gettys
, 1986
Xlib
qui
transforme les ordres graphiques ou lectures d'événements en
écritures ou lectures sur une connexion par socket
Expose
pour
recalculer des rectangles. On minimise les bitmaps cachés.
TIT .X WINDOW system : C library and protocol reference SLA .Robert W. Scheifler, James Gettys, Ron Newman. - [s.l.] : .Digital Press, 1988. - XXIX-701 p. ; 24 cm. TIT .X WINDOW: Applications programming SLA .Eric F. Johnson, Kevin Reichard . MIS press, 1989.
Sun Microsystems, 1984
/etc/mount
ou df
permettent de voir
sur cubitus
des lignes:
poly.polytechnique.fr:/usr/users/cie1 196895 157405 19801 89% /usr/users/cie1 ...Le sous-arbre
/usr/users/cie1
est en fait le sous-arbre
/usr/users/cie1
de la machine poly.polytechnique.fr
lui même
monté sur le device /dev/ra4d
.
read()
, write()
sont transformés en requêtes au
serveur nfsd
de poly
. En fait les clients sont 4
processus systèmes biod
du noyau de cubitus
.
/et{c
/exports} donne la liste des droits de montage de
fichiers, côté serveur. showmounts
montre les clients actifs.
/et{c
/hosts} où on trouve aussi leur nom symbolique.
(protocole, adresse_1, port_1, adresse_2, port_2)
Par exemple:
(tcp, 128.10.0.3, 1500, 128.10.0.7, 21)
/etc/services
)
sockets
AF_UNIX
: nom de fichier.
struct sockaddr_un { short sun_family; char sun_path[108-4]; #endif };
AF_INET
: numéro internet 192.48.98.1 ou
poly.polytechnique.fr. Les fonctions gethostbyaddr
ou
gethostbyname
donnent l'adresse.
struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; };
sockets
netstat -a
permet de voir les services
courants. Exemple: 6000 pour X-windows, ...
kpr% date; netstat -a |head -10 Sun Feb 23 17:27:44 MET 1992 Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp 0 0 kpriss.inria.fr.1022 margaux.inria.fr.login ESTABLISHED tcp 0 0 kpriss.inria.fr.6000 toul.inria.fr.1503 ESTABLISHED tcp 0 0 kpriss.inria.fr.1018 givry.inria.fr.login ESTABLISHED tcp 0 0 kpriss.inria.fr.6000 toul.inria.fr.1497 ESTABLISHED tcp 0 0 kpriss.inria.fr.1019 poly.polytechniq.login ESTABLISHED tcp 0 0 kpriss.inria.fr.1020 toul.inria.fr.login ESTABLISHED ... tcp 0 0 *.6000 *.* LISTEN tcp 0 0 *.printer *.* LISTEN tcp 0 0 *.shell *.* LISTEN tcp 0 0 *.login *.* LISTEN ... udp 0 0 *.1062 *.* udp 0 0 *.ntalk *.* udp 0 0 *.talk *.* udp 0 0 *.biff *.* ... Active UNIX domain sockets Address Type Recv-Q Send-Q Inode Conn Refs Nextref Addr c37b8280 stream 0 0 0 c36e5200 0 0 ... c3736d80 stream 0 0 8017b0a0 0 0 0 /dev/printer c3736c00 dgram 0 0 8017bce0 0 0 0 /dev/elcscntls ckt c3736a80 stream 0 0 8017cdb8 0 0 0 /tmp/.X11-unix /X0}
sockets
sont apparues (Leffler,
...
). Leurs buts:
/usr/bin/Xwd
pour X-windows. Les clients sont
les utilitaires X: xterm, xclock, xload, ...
fd=socket(domaine, type, protocole); bind(fd, adresse, longueur_adresse); listen(fd, max_servis); fd1 = accept (fd, adresse_client, longueur_adresse_client); read (fd1, ..., ...); write {fd1, ..., ...);
fd = socket(domaine, type, protocole); connect (fd, adresse_serveur, longueur_adresse_serveur); read (fd, ..., ...); write (fd, ..., ...);
socket
crée un file descriptor décrivant une
domaine unix AF_UNIX
ou internet
AF_INET
, le type datagramme SOCK_DGRAM
ou
circuit virtuel SOCK_STREAM
, un protocole (en général 0
pour celui par défaut).
bind
attache une adresse (port) à un socket
. Les
adresses >= 1024 sont réservées au super-utilisateur.
listen
donne la longueur maximale de la queue d'attente
des demandes de connexions non servies (par exemple 5). Très utile car en
général le serveur fait fork() dès qu'il accepte une
connexion.
accept
est une fonction bloquante qui attend les demandes
de connexion et qui retourne un nouveau file descriptor qui
permettra de faire les entrées/sorties sur la nouvelle connexion.
connect
est l'opération duale pour le client. On se sert
du file descriptor existant pour la nouvelle connexion.
int fd, newfd; if ((fd = socket(...)) < 0) erreur ("ouverture socket"); if (bind(fd, ...) < 0) erreur ("bind"); if (listen (fd, 5) < 0) erreur ("listen"); for (; ;) { if ((newfd = accept (fd, ...)) < 0) /* blocant */ erreur ("accept"); if (fork() == 0) { close (fd); .... /* on continue avec newfd */ exit (0); } close (newfd); }
AF_UNIX
#include <sys/un.h> #define X_UNIX_PATH "/tmp/.X11-unix/X" Display *XOpenDisplay (register char *display) { struct sockaddr_un unaddr; /* UNIX socket data block */ struct sockaddr *addr; /* generic socket pointer */ int addrlen; /* length of addr */ ... unaddr.sun_family = AF_UNIX; sprintf (unaddr.sun_path, "%s%d", X_UNIX_PATH, idisplay); addr = (struct sockaddr *) &unaddr; addrlen = strlen(unaddr.sun_path) + sizeof(unaddr.sun_family); if ((fd = socket (((int) addr->sa_family, SOCK_STREAM, 0)) < 0) { return -1; } if (connect (fd, addr, addrlen) < 0) { int olderrno = errno; (void) close (fd); errno = olderrno; return -1; } ... dpy->fd = fd; return (dpy); }
AF_UNIX
#define X_UNIX_PATH "/tmp/.X11-unix/X" static struct sockaddr_un unsock; static int open_unix_socket () { int oldUmask; int request; unsock.sun_family = AF_UNIX; oldUmask = umask (0); strcpy (unsock.sun_path, X_UNIX_PATH); strcat (unsock.sun_path, display); unlink (unsock.sun_path); if ((request = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) { Error ("Creating Unix socket"); return -1; } if (bind(request, (struct sockaddr *)&unsock, strlen(unsock.sun_path)+2)) { Error ("Binding Unix socket"); close (request); return -1; } if (listen (request, 5)) { Error ("Unix Listening"); close (request); return -1; } (void)umask(oldUmask); return request; }
AF_UNIX
long WellKnownConnections; /* Listener mask */ long LastSelectMask[mskcnt]; /* mask returned from last select call */ void EstablishNewConnections() { long readyconnections; /* mask of listeners that are ready */ int curconn; /* fd of listener that's ready */ register int newconn; /* fd of new client */ long connect_time; register int i; register ClientPtr client; readyconnections = (LastSelectMask[0] & WellKnownConnections); if (!readyconnections) return; connect_time = GetTimeInMillis(); /* kill off stragglers */ for (i=1; i<currentMaxClients; i++) { ... } while (readyconnections) { curconn = ffs (readyconnections) - 1; readyconnections &= ~(1 << curconn); if ((newconn = accept (curconn, (struct sockaddr *) NULL, (int *)NULL)) < 0) continue; fcntl (newconn, F_SETFL, FNDELAY); ... } ... }
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <errno.h> #include <string.h> extern int errno; char *usage = "usage : netclient[-q]"; int main (argc, argv) int argc; char *argv []; { long i; int s; struct hostent *serverhost; struct sockaddr_in serveraddr; struct servent *service; int service_num; char buffer [8192]; int nbytes, sent; char addr [4]; int ret; int use_direct_addr; int quiet = 0; if (argc != 3 && argc != 4){ fprintf (stderr, "%s\n", usage); exit (2); } if (argc == 4){ if (!strcmp (argv [3], "-q")){ quiet = 1; }else{ fprintf (stderr, "%s\n", usage); exit (2); } } s = socket (AF_INET, SOCK_STREAM, 0); if (s == -1){ perror ("socket"); exit (3); } if (s >= 8 * sizeof (int)){ fprintf (stderr, "socket file number is too high\n"); exit (3); } if (argv [1] [0] >= '0' && argv [1] [0] <= '9'){ int a0, a1, a2, a3; sscanf (argv [1], "%d.%d.%d.%d", &a0, &a1, &a2, &a3); addr [0] = a0; addr [1] = a1; addr [2] = a2; addr [3] = a3; serverhost = gethostbyaddr (addr, 4, AF_INET); if (serverhost == NULL){ use_direct_addr = 1; }else{ use_direct_addr = 0; } }else{ serverhost = gethostbyname (argv [1]); use_direct_addr = 0; } if (serverhost == NULL && !use_direct_addr){ fprintf (stderr, "unknown host: %s\n", argv [1]); exit (3); } if (argv [2] [0] >= '0' && argv [2] [0] <= '9'){ service_num = htons (atoi (argv [2])); }else{ service = getservbyname (argv [2], NULL); if (service == NULL){ fprintf (stderr, "unknown service: %s\n", argv [2]); exit (3); } service_num = service->s_port; } serveraddr.sin_family = serverhost->h_addrtype; serveraddr.sin_port = service_num; if (use_direct_addr){ bcopy (addr, &serveraddr.sin_addr, 4); }else{ bcopy (serverhost->h_addr, &serveraddr.sin_addr, serverhost->h_length); } if (connect (s, &serveraddr, sizeof (serveraddr)) == -1){ if (errno == ECONNREFUSED){ if (!quiet) perror ("connect"); exit (100); }else{ perror ("connect"); exit (3); } } switch (fork ()){ case -1: perror ("fork"); exit (3); case 0: close (1); while (1){ nbytes = read (0, buffer, 8192); if (nbytes == -1){ perror ("read (stdin)"); shutdown (s, 1); exit (3); } if (nbytes == 0){ shutdown (s, 1); exit (0); } for (sent = 0; sent < nbytes;){ ret = write (s, buffer + sent, nbytes - sent); if (ret == -1){ perror ("write (socket)"); exit (3); } sent += ret; } } default: close (0); while (1){ nbytes = read (s, buffer, 8192); if (nbytes == -1){ perror ("read (socket)"); close (1); wait (NULL); exit (3); } if (nbytes == 0){ close (1); wait (NULL); exit (0); } for (sent = 0; sent < nbytes;){ ret = write (1, buffer + sent, nbytes - sent); if (ret == -1){ perror ("write (stdout)"); exit (3); } sent += ret; } } } }
talk
. Attention:
avec les datagrammes, les messages ne sont pas sûrs d'arriver,
ni dans l'ordre. Le protocole est plus bas, donc plus efficace.
recvfrom()
et sendto()
sont alors utilisés.
udp
(User Datagram Protocol)
talk
se sert de 2 sockets: contrôle en datagrammes
(avec le démon talkd
), communication en mode connecté
(avec l'interlocuteur qui fait talk
).
for (;;) { extern int errno; fromlen = sizeof(from); cc = recvfrom(0, (char *)&request, sizeof (request), 0, &from, &fromlen); if (cc != sizeof(request)) { if (cc < 0 && errno != EINTR) perror("recvfrom"); continue; } lastmsgtime = time(0); swapmsg(&request); if (debug) print_request(&request); process_request(&request, &response); /* can block here, is this what I want? */ cc = sendto(sockt, (char *) &response, sizeof (response), 0, &request.ctl_addr, sizeof (request.ctl_addr)); if (cc != sizeof(response)) perror("sendto"); }
/* open the ctl socket */ /* client: ctl.c */ open_ctl() { int length; ctl_addr.sin_port = 0; ctl_addr.sin_addr = my_machine_addr; ctl_sockt = socket(AF_INET, SOCK_DGRAM, 0); if (ctl_sockt <= 0) p_error("Bad socket"); if (bind(ctl_sockt, &ctl_addr, sizeof(ctl_addr), 0) != 0) p_error("Couldn't bind to control socket"); length = sizeof(ctl_addr); if (getsockname(ctl_sockt, &ctl_addr, &length) == -1) p_error("Bad address for ctl socket"); } kpr% talk levy@toul ... kpr% netstat -a Active Internet connections (including servers) tcp 0 0 kpriss.inria.fr.1042 toul.inria.fr.1026 ESTABLISHED udp 0 0 kpriss.inria.fr.1416 *.* tou% talk levy@kpriss tou% netstat -a Active Internet connections (including servers) Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp 0 0 toul.inria.fr.1026 kpriss.inria.fr.1042 ESTABLISHED udp 0 0 toul.inria.fr.1143 *.*
AF_UNIX
% setenv DISPLAY unix:0ou bien dans
lpd
.
#define SOCKETNAME "/dev/printer" /* * Restart all the printers. */ startup(); (void) unlink(SOCKETNAME); funix = socket(AF_UNIX, SOCK_STREAM, 0); if (funix < 0) { logerr("socket"); exit(1); } #define mask(s) (1 << ((s) - 1)) omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); signal(SIGHUP, cleanup); signal(SIGINT, cleanup); signal(SIGQUIT, cleanup); signal(SIGTERM, cleanup); sun.sun_family = AF_UNIX; strcpy(sun.sun_path, SOCKETNAME); if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) { logerr("unix domain bind"); exit(1); } sigsetmask(omask); defreadfds = 1 << funix; listen(funix, 5); finet = socket(AF_INET, SOCK_STREAM, 0);