Annotation of 43BSDReno/share/doc/ps1/08.ipc/4.t, revision 1.1.1.1

1.1       root        1: .\" Copyright (c) 1986 The Regents of the University of California.
                      2: .\" All rights reserved.
                      3: .\"
                      4: .\" Redistribution and use in source and binary forms are permitted
                      5: .\" provided that the above copyright notice and this paragraph are
                      6: .\" duplicated in all such forms and that any documentation,
                      7: .\" advertising materials, and other materials related to such
                      8: .\" distribution and use acknowledge that the software was developed
                      9: .\" by the University of California, Berkeley.  The name of the
                     10: .\" University may not be used to endorse or promote products derived
                     11: .\" from this software without specific prior written permission.
                     12: .\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
                     13: .\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
                     14: .\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                     15: .\"
                     16: .\"    @(#)4.t 1.4 (Berkeley) 3/7/89
                     17: .\"
                     18: .\".ds RH "Client/Server Model
                     19: .bp
                     20: .nr H1 4
                     21: .nr H2 0
                     22: .sp 8i
                     23: .bp
                     24: .LG
                     25: .B
                     26: .ce
                     27: 4. CLIENT/SERVER MODEL
                     28: .sp 2
                     29: .R
                     30: .NL
                     31: .PP
                     32: The most commonly used paradigm in constructing distributed applications
                     33: is the client/server model.  In this scheme client applications request
                     34: services from a server process.  This implies an asymmetry in establishing
                     35: communication between the client and server which has been examined
                     36: in section 2.  In this section we will look more closely at the interactions
                     37: between client and server, and consider some of the problems in developing
                     38: client and server applications.
                     39: .PP
                     40: The client and server require a well known set of conventions before
                     41: service may be rendered (and accepted).  This set of conventions
                     42: comprises a protocol which must be implemented at both ends of a
                     43: connection.  Depending on the situation, the protocol may be symmetric
                     44: or asymmetric.  In a symmetric protocol, either side may play the 
                     45: master or slave roles.  In an asymmetric protocol, one side is
                     46: immutably recognized as the master, with the other as the slave.  
                     47: An example of a symmetric protocol is the TELNET protocol used in
                     48: the Internet for remote terminal emulation.  An example
                     49: of an asymmetric protocol is the Internet file transfer protocol,
                     50: FTP.  No matter whether the specific protocol used in obtaining
                     51: a service is symmetric or asymmetric, when accessing a service there
                     52: is a \*(lqclient process\*(rq and a \*(lqserver process\*(rq.  We
                     53: will first consider the properties of server processes, then
                     54: client processes.
                     55: .PP
                     56: A server process normally listens at a well known address for
                     57: service requests.  That is, the server process remains dormant
                     58: until a connection is requested by a client's connection
                     59: to the server's address.  At such a time
                     60: the server process ``wakes up'' and services the client,
                     61: performing whatever appropriate actions the client requests of it.
                     62: .PP
                     63: Alternative schemes which use a service server
                     64: may be used to eliminate a flock of server processes clogging the
                     65: system while remaining dormant most of the time.  For Internet
                     66: servers in 4.3BSD,
                     67: this scheme has been implemented via \fIinetd\fP, the so called
                     68: ``internet super-server.''  \fIInetd\fP listens at a variety
                     69: of ports, determined at start-up by reading a configuration file.
                     70: When a connection is requested to a port on which \fIinetd\fP is
                     71: listening, \fIinetd\fP executes the appropriate server program to handle the
                     72: client.  With this method, clients are unaware that an
                     73: intermediary such as \fIinetd\fP has played any part in the
                     74: connection.  \fIInetd\fP will be described in more detail in
                     75: section 5.
                     76: .PP
                     77: A similar alternative scheme is used by most Xerox services.  In general,
                     78: the Courier dispatch process (if used) accepts connections from
                     79: processes requesting services of some sort or another.  The client
                     80: processes request a particular <program number, version number, procedure
                     81: number> triple.  If the dispatcher knows of such a program, it is
                     82: started to handle the request; if not, an error is reported to the
                     83: client.  In this way, only one port is required to service a large
                     84: variety of different requests.  Again, the Courier facilities are
                     85: not available without the use and installation of the Courier
                     86: compiler.  The information presented in this section applies only
                     87: to NS clients and services that do not use Courier.
                     88: .NH 2
                     89: Servers
                     90: .PP
                     91: In 4.3BSD most servers are accessed at well known Internet addresses
                     92: or UNIX domain names.  For
                     93: example, the remote login server's main loop is of the form shown
                     94: in Figure 2.
                     95: .KF
                     96: .if t .ta .5i 1.0i 1.5i 2.0i 2.5i 3.0i 3.5i
                     97: .if n .ta .7i 1.4i 2.1i 2.8i 3.5i 4.2i 4.9i
                     98: .sp 0.5i
                     99: .DS
                    100: main(argc, argv)
                    101:        int argc;
                    102:        char *argv[];
                    103: {
                    104:        int f;
                    105:        struct sockaddr_in from;
                    106:        struct servent *sp;
                    107: 
                    108:        sp = getservbyname("login", "tcp");
                    109:        if (sp == NULL) {
                    110:                fprintf(stderr, "rlogind: tcp/login: unknown service\en");
                    111:                exit(1);
                    112:        }
                    113:        ...
                    114: #ifndef DEBUG
                    115:        /* Disassociate server from controlling terminal */
                    116:        ...
                    117: #endif
                    118: 
                    119:        sin.sin_port = sp->s_port;      /* Restricted port -- see section 5 */
                    120:        ...
                    121:        f = socket(AF_INET, SOCK_STREAM, 0);
                    122:        ...
                    123:        if (bind(f, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
                    124:                ...
                    125:        }
                    126:        ...
                    127:        listen(f, 5);
                    128:        for (;;) {
                    129:                int g, len = sizeof (from);
                    130: 
                    131:                g = accept(f, (struct sockaddr *) &from, &len);
                    132:                if (g < 0) {
                    133:                        if (errno != EINTR)
                    134:                                syslog(LOG_ERR, "rlogind: accept: %m");
                    135:                        continue;
                    136:                }
                    137:                if (fork() == 0) {
                    138:                        close(f);
                    139:                        doit(g, &from);
                    140:                }
                    141:                close(g);
                    142:        }
                    143: }
                    144: .DE
                    145: .ce
                    146: Figure 2.  Remote login server.
                    147: .sp 0.5i
                    148: .KE
                    149: .PP
                    150: The first step taken by the server is look up its service
                    151: definition:
                    152: .sp 1
                    153: .nf
                    154: .in +5
                    155: .if t .ta .5i 1.0i 1.5i 2.0i
                    156: .if n .ta .7i 1.4i 2.1i 2.8i
                    157: sp = getservbyname("login", "tcp");
                    158: if (sp == NULL) {
                    159:        fprintf(stderr, "rlogind: tcp/login: unknown service\en");
                    160:        exit(1);
                    161: }
                    162: .sp 1
                    163: .in -5
                    164: .fi
                    165: The result of the \fIgetservbyname\fP call
                    166: is used in later portions of the code to
                    167: define the Internet port at which it listens for service
                    168: requests (indicated by a connection).
                    169: .KS
                    170: .PP
                    171: Step two is to disassociate the server from the controlling
                    172: terminal of its invoker:
                    173: .DS
                    174:        for (i = 0; i < 3; ++i)
                    175:                close(i);
                    176: 
                    177:        open("/", O_RDONLY);
                    178:        dup2(0, 1);
                    179:        dup2(0, 2);
                    180: 
                    181:        i = open("/dev/tty", O_RDWR);
                    182:        if (i >= 0) {
                    183:                ioctl(i, TIOCNOTTY, 0);
                    184:                close(i);
                    185:        }
                    186: .DE
                    187: .KE
                    188: This step is important as the server will
                    189: likely not want to receive signals delivered to the process
                    190: group of the controlling terminal.  Note, however, that
                    191: once a server has disassociated itself it can no longer
                    192: send reports of errors to a terminal, and must log errors
                    193: via \fIsyslog\fP.
                    194: .PP
                    195: Once a server has established a pristine environment, it
                    196: creates a socket and begins accepting service requests.
                    197: The \fIbind\fP call is required to insure the server listens
                    198: at its expected location.  It should be noted that the
                    199: remote login server listens at a restricted port number, and must
                    200: therefore be run
                    201: with a user-id of root.
                    202: This concept of a ``restricted port number'' is 4BSD
                    203: specific, and is covered in section 5.
                    204: .PP
                    205: The main body of the loop is fairly simple:
                    206: .DS
                    207: .if t .ta .5i 1.0i 1.5i 2.0i
                    208: .if n .ta .7i 1.4i 2.1i 2.8i
                    209: for (;;) {
                    210:        int g, len = sizeof (from);
                    211: 
                    212:        g = accept(f, (struct sockaddr *)&from, &len);
                    213:        if (g < 0) {
                    214:                if (errno != EINTR)
                    215:                        syslog(LOG_ERR, "rlogind: accept: %m");
                    216:                continue;
                    217:        }
                    218:        if (fork() == 0) {      /* Child */
                    219:                close(f);
                    220:                doit(g, &from);
                    221:        }
                    222:        close(g);               /* Parent */
                    223: }
                    224: .DE
                    225: An \fIaccept\fP call blocks the server until
                    226: a client requests service.  This call could return a
                    227: failure status if the call is interrupted by a signal
                    228: such as SIGCHLD (to be discussed in section 5).  Therefore,
                    229: the return value from \fIaccept\fP is checked to insure
                    230: a connection has actually been established, and
                    231: an error report is logged via \fIsyslog\fP if an error
                    232: has occurred.
                    233: .PP
                    234: With a connection
                    235: in hand, the server then forks a child process and invokes
                    236: the main body of the remote login protocol processing.  Note
                    237: how the socket used by the parent for queuing connection
                    238: requests is closed in the child, while the socket created as
                    239: a result of the \fIaccept\fP is closed in the parent.  The
                    240: address of the client is also handed the \fIdoit\fP routine
                    241: because it requires it in authenticating clients.
                    242: .NH 2
                    243: Clients
                    244: .PP
                    245: The client side of the remote login service was shown
                    246: earlier in Figure 1.
                    247: One can see the separate, asymmetric roles of the client
                    248: and server clearly in the code.  The server is a passive entity,
                    249: listening for client connections, while the client process is
                    250: an active entity, initiating a connection when invoked.  
                    251: .PP
                    252: Let us consider more closely the steps taken
                    253: by the client remote login process.  As in the server process,
                    254: the first step is to locate the service definition for a remote
                    255: login:
                    256: .DS
                    257: sp = getservbyname("login", "tcp");
                    258: if (sp == NULL) {
                    259:        fprintf(stderr, "rlogin: tcp/login: unknown service\en");
                    260:        exit(1);
                    261: }
                    262: .DE
                    263: Next the destination host is looked up with a
                    264: \fIgethostbyname\fP call:
                    265: .DS
                    266: hp = gethostbyname(argv[1]);
                    267: if (hp == NULL) {
                    268:        fprintf(stderr, "rlogin: %s: unknown host\en", argv[1]);
                    269:        exit(2);
                    270: }
                    271: .DE
                    272: With this accomplished, all that is required is to establish a
                    273: connection to the server at the requested host and start up the
                    274: remote login protocol.  The address buffer is cleared, then filled
                    275: in with the Internet address of the foreign host and the port
                    276: number at which the login process resides on the foreign host:
                    277: .DS
                    278: bzero((char *)&server, sizeof (server));
                    279: bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length);
                    280: server.sin_family = hp->h_addrtype;
                    281: server.sin_port = sp->s_port;
                    282: .DE
                    283: A socket is created, and a connection initiated.  Note
                    284: that \fIconnect\fP implicitly performs a \fIbind\fP
                    285: call, since \fIs\fP is unbound.
                    286: .DS
                    287: s = socket(hp->h_addrtype, SOCK_STREAM, 0);
                    288: if (s < 0) {
                    289:        perror("rlogin: socket");
                    290:        exit(3);
                    291: }
                    292:  ...
                    293: if (connect(s, (struct sockaddr *) &server, sizeof (server)) < 0) {
                    294:        perror("rlogin: connect");
                    295:        exit(4);
                    296: }
                    297: .DE
                    298: The details of the remote login protocol will not be considered here.
                    299: .NH 2
                    300: Connectionless servers
                    301: .PP
                    302: While connection-based services are the norm, some services
                    303: are based on the use of datagram sockets.  One, in particular,
                    304: is the \*(lqrwho\*(rq service which provides users with status
                    305: information for hosts connected to a local area
                    306: network.  This service, while predicated on the ability to
                    307: \fIbroadcast\fP information to all hosts connected to a particular
                    308: network, is of interest as an example usage of datagram sockets.
                    309: .PP
                    310: A user on any machine running the rwho server may find out
                    311: the current status of a machine with the \fIruptime\fP(1) program.
                    312: The output generated is illustrated in Figure 3.
                    313: .KF
                    314: .DS B
                    315: .TS
                    316: l r l l l l l.
                    317: arpa   up      9:45,   5 users, load   1.15,   1.39,   1.31
                    318: cad    up      2+12:04,        8 users, load   4.67,   5.13,   4.59
                    319: calder up      10:10,  0 users, load   0.27,   0.15,   0.14
                    320: dali   up      2+06:28,        9 users, load   1.04,   1.20,   1.65
                    321: degas  up      25+09:48,       0 users, load   1.49,   1.43,   1.41
                    322: ear    up      5+00:05,        0 users, load   1.51,   1.54,   1.56
                    323: ernie  down    0:24
                    324: esvax  down    17:04
                    325: ingres down    0:26
                    326: kim    up      3+09:16,        8 users, load   2.03,   2.46,   3.11
                    327: matisse        up      3+06:18,        0 users, load   0.03,   0.03,   0.05
                    328: medea  up      3+09:39,        2 users, load   0.35,   0.37,   0.50
                    329: merlin down    19+15:37
                    330: miro   up      1+07:20,        7 users, load   4.59,   3.28,   2.12
                    331: monet  up      1+00:43,        2 users, load   0.22,   0.09,   0.07
                    332: oz     down    16:09
                    333: statvax        up      2+15:57,        3 users, load   1.52,   1.81,   1.86
                    334: ucbvax up      9:34,   2 users, load   6.08,   5.16,   3.28
                    335: .TE
                    336: .DE
                    337: .ce
                    338: Figure 3. ruptime output.
                    339: .sp
                    340: .KE
                    341: .PP
                    342: Status information for each host is periodically broadcast
                    343: by rwho server processes on each machine.  The same server
                    344: process also receives the status information and uses it
                    345: to update a database.  This database is then interpreted
                    346: to generate the status information for each host.  Servers
                    347: operate autonomously, coupled only by the local network and
                    348: its broadcast capabilities.
                    349: .PP
                    350: Note that the use of broadcast for such a task is fairly inefficient,
                    351: as all hosts must process each message, whether or not using an rwho server.
                    352: Unless such a service is sufficiently universal and is frequently used,
                    353: the expense of periodic broadcasts outweighs the simplicity.
                    354: .PP
                    355: The rwho server, in a simplified form, is pictured in Figure
                    356: 4.  There are two separate tasks performed by the server.  The
                    357: first task is to act as a receiver of status information broadcast
                    358: by other hosts on the network.  This job is carried out in the
                    359: main loop of the program.  Packets received at the rwho port
                    360: are interrogated to insure they've been sent by another rwho
                    361: server process, then are time stamped with their arrival time
                    362: and used to update a file indicating the status of the host.
                    363: When a host has not been heard from for an extended period of
                    364: time, the database interpretation routines assume the host is
                    365: down and indicate such on the status reports.  This algorithm
                    366: is prone to error as a server may be down while a host is actually
                    367: up, but serves our current needs.
                    368: .KF
                    369: .DS
                    370: .if t .ta .5i 1.0i 1.5i 2.0i
                    371: .if n .ta .7i 1.4i 2.1i 2.8i
                    372: main()
                    373: {
                    374:        ...
                    375:        sp = getservbyname("who", "udp");
                    376:        net = getnetbyname("localnet");
                    377:        sin.sin_addr = inet_makeaddr(INADDR_ANY, net);
                    378:        sin.sin_port = sp->s_port;
                    379:        ...
                    380:        s = socket(AF_INET, SOCK_DGRAM, 0);
                    381:        ...
                    382:        on = 1;
                    383:        if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
                    384:                syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
                    385:                exit(1);
                    386:        }
                    387:        bind(s, (struct sockaddr *) &sin, sizeof (sin));
                    388:        ...
                    389:        signal(SIGALRM, onalrm);
                    390:        onalrm();
                    391:        for (;;) {
                    392:                struct whod wd;
                    393:                int cc, whod, len = sizeof (from);
                    394: 
                    395:                cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
                    396:                    (struct sockaddr *)&from, &len);
                    397:                if (cc <= 0) {
                    398:                        if (cc < 0 && errno != EINTR)
                    399:                                syslog(LOG_ERR, "rwhod: recv: %m");
                    400:                        continue;
                    401:                }
                    402:                if (from.sin_port != sp->s_port) {
                    403:                        syslog(LOG_ERR, "rwhod: %d: bad from port",
                    404:                                ntohs(from.sin_port));
                    405:                        continue;
                    406:                }
                    407:                ...
                    408:                if (!verify(wd.wd_hostname)) {
                    409:                        syslog(LOG_ERR, "rwhod: malformed host name from %x",
                    410:                                ntohl(from.sin_addr.s_addr));
                    411:                        continue;
                    412:                }
                    413:                (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
                    414:                whod = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
                    415:                ...
                    416:                (void) time(&wd.wd_recvtime);
                    417:                (void) write(whod, (char *)&wd, cc);
                    418:                (void) close(whod);
                    419:        }
                    420: }
                    421: .DE
                    422: .ce
                    423: Figure 4.  rwho server.
                    424: .sp
                    425: .KE
                    426: .PP
                    427: The second task performed by the server is to supply information
                    428: regarding the status of its host.  This involves periodically
                    429: acquiring system status information, packaging it up in a message
                    430: and broadcasting it on the local network for other rwho servers
                    431: to hear.  The supply function is triggered by a timer and 
                    432: runs off a signal.  Locating the system status
                    433: information is somewhat involved, but uninteresting.  Deciding
                    434: where to transmit the resultant packet
                    435: is somewhat problematical, however.
                    436: .PP
                    437: Status information must be broadcast on the local network.
                    438: For networks which do not support the notion of broadcast another
                    439: scheme must be used to simulate or
                    440: replace broadcasting.  One possibility is to enumerate the
                    441: known neighbors (based on the status messages received
                    442: from other rwho servers).  This, unfortunately,
                    443: requires some bootstrapping information,
                    444: for a server will have no idea what machines are its
                    445: neighbors until it receives status messages from them.
                    446: Therefore, if all machines on a net are freshly booted,
                    447: no machine will have any
                    448: known neighbors and thus never receive, or send, any status information.
                    449: This is the identical problem faced by the routing table management
                    450: process in propagating routing status information.  The standard
                    451: solution, unsatisfactory as it may be, is to inform one or more servers
                    452: of known neighbors and request that they always communicate with
                    453: these neighbors.  If each server has at least one neighbor supplied
                    454: to it, status information may then propagate through
                    455: a neighbor to hosts which
                    456: are not (possibly) directly neighbors.  If the server is able to
                    457: support networks which provide a broadcast capability, as well as
                    458: those which do not, then networks with an
                    459: arbitrary topology may share status information*.
                    460: .FS
                    461: * One must, however, be concerned about \*(lqloops\*(rq.
                    462: That is, if a host is connected to multiple networks, it
                    463: will receive status information from itself.  This can lead
                    464: to an endless, wasteful, exchange of information.
                    465: .FE
                    466: .PP
                    467: It is important that software operating in a distributed
                    468: environment not have any site-dependent information compiled into it.
                    469: This would require a separate copy of the server at each host and
                    470: make maintenance a severe headache.  4.3BSD attempts to isolate
                    471: host-specific information from applications by providing system
                    472: calls which return the necessary information*.
                    473: .FS
                    474: * An example of such a system call is the \fIgethostname\fP(2)
                    475: call which returns the host's \*(lqofficial\*(rq name.
                    476: .FE
                    477: A mechanism exists, in the form of an \fIioctl\fP call,
                    478: for finding the collection
                    479: of networks to which a host is directly connected.
                    480: Further, a local network broadcasting mechanism
                    481: has been implemented at the socket level.
                    482: Combining these two features allows a process
                    483: to broadcast on any directly connected local
                    484: network which supports the notion of broadcasting
                    485: in a site independent manner.  This allows 4.3BSD
                    486: to solve the problem of deciding how to propagate
                    487: status information in the case of \fIrwho\fP, or
                    488: more generally in broadcasting:
                    489: Such status information is broadcast to connected
                    490: networks at the socket level, where the connected networks
                    491: have been obtained via the appropriate \fIioctl\fP
                    492: calls.
                    493: The specifics of
                    494: such broadcastings are complex, however, and will
                    495: be covered in section 5.

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.