1 .\" Copyright (c) 1986, 1993
2 .\" The Regents of the University of California. All rights reserved.
4 .\" Redistribution and use in source and binary forms, with or without
5 .\" modification, are permitted provided that the following conditions
7 .\" 1. Redistributions of source code must retain the above copyright
8 .\" notice, this list of conditions and the following disclaimer.
9 .\" 2. Redistributions in binary form must reproduce the above copyright
10 .\" notice, this list of conditions and the following disclaimer in the
11 .\" documentation and/or other materials provided with the distribution.
12 .\" 3. All advertising materials mentioning features or use of this software
13 .\" must display the following acknowledgement:
14 .\" This product includes software developed by the University of
15 .\" California, Berkeley and its contributors.
16 .\" 4. Neither the name of the University nor the names of its contributors
17 .\" may be used to endorse or promote products derived from this software
18 .\" without specific prior written permission.
20 .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 .\" @(#)4.t 8.1 (Berkeley) 6/8/93
35 .\".ds RH "Client/Server Model
44 4. CLIENT/SERVER MODEL
49 The most commonly used paradigm in constructing distributed applications
50 is the client/server model. In this scheme client applications request
51 services from a server process. This implies an asymmetry in establishing
52 communication between the client and server which has been examined
53 in section 2. In this section we will look more closely at the interactions
54 between client and server, and consider some of the problems in developing
55 client and server applications.
57 The client and server require a well known set of conventions before
58 service may be rendered (and accepted). This set of conventions
59 comprises a protocol which must be implemented at both ends of a
60 connection. Depending on the situation, the protocol may be symmetric
61 or asymmetric. In a symmetric protocol, either side may play the
62 master or slave roles. In an asymmetric protocol, one side is
63 immutably recognized as the master, with the other as the slave.
64 An example of a symmetric protocol is the TELNET protocol used in
65 the Internet for remote terminal emulation. An example
66 of an asymmetric protocol is the Internet file transfer protocol,
67 FTP. No matter whether the specific protocol used in obtaining
68 a service is symmetric or asymmetric, when accessing a service there
69 is a \*(lqclient process\*(rq and a \*(lqserver process\*(rq. We
70 will first consider the properties of server processes, then
73 A server process normally listens at a well known address for
74 service requests. That is, the server process remains dormant
75 until a connection is requested by a client's connection
76 to the server's address. At such a time
77 the server process ``wakes up'' and services the client,
78 performing whatever appropriate actions the client requests of it.
80 Alternative schemes which use a service server
81 may be used to eliminate a flock of server processes clogging the
82 system while remaining dormant most of the time. For Internet
84 this scheme has been implemented via \fIinetd\fP, the so called
85 ``internet super-server.'' \fIInetd\fP listens at a variety
86 of ports, determined at start-up by reading a configuration file.
87 When a connection is requested to a port on which \fIinetd\fP is
88 listening, \fIinetd\fP executes the appropriate server program to handle the
89 client. With this method, clients are unaware that an
90 intermediary such as \fIinetd\fP has played any part in the
91 connection. \fIInetd\fP will be described in more detail in
94 A similar alternative scheme is used by most Xerox services. In general,
95 the Courier dispatch process (if used) accepts connections from
96 processes requesting services of some sort or another. The client
97 processes request a particular <program number, version number, procedure
98 number> triple. If the dispatcher knows of such a program, it is
99 started to handle the request; if not, an error is reported to the
100 client. In this way, only one port is required to service a large
101 variety of different requests. Again, the Courier facilities are
102 not available without the use and installation of the Courier
103 compiler. The information presented in this section applies only
104 to NS clients and services that do not use Courier.
108 In 4.4BSD most servers are accessed at well known Internet addresses
109 or UNIX domain names. For
110 example, the remote login server's main loop is of the form shown
113 .if t .ta .5i 1.0i 1.5i 2.0i 2.5i 3.0i 3.5i
114 .if n .ta .7i 1.4i 2.1i 2.8i 3.5i 4.2i 4.9i
122 struct sockaddr_in from;
125 sp = getservbyname("login", "tcp");
127 fprintf(stderr, "rlogind: login/tcp: unknown service\en");
132 /* Disassociate server from controlling terminal */
136 sin.sin_port = sp->s_port; /* Restricted port -- see section 5 */
138 f = socket(AF_INET, SOCK_STREAM, 0);
140 if (bind(f, (struct sockaddr *) &sin, sizeof (sin)) < 0) {
146 int g, len = sizeof (from);
148 g = accept(f, (struct sockaddr *) &from, &len);
151 syslog(LOG_ERR, "rlogind: accept: %m");
163 Figure 2. Remote login server.
167 The first step taken by the server is look up its service
172 .if t .ta .5i 1.0i 1.5i 2.0i
173 .if n .ta .7i 1.4i 2.1i 2.8i
174 sp = getservbyname("login", "tcp");
176 fprintf(stderr, "rlogind: login/tcp: unknown service\en");
182 The result of the \fIgetservbyname\fP call
183 is used in later portions of the code to
184 define the Internet port at which it listens for service
185 requests (indicated by a connection).
188 Step two is to disassociate the server from the controlling
189 terminal of its invoker:
191 for (i = 0; i < 3; ++i)
198 i = open("/dev/tty", O_RDWR);
200 ioctl(i, TIOCNOTTY, 0);
205 This step is important as the server will
206 likely not want to receive signals delivered to the process
207 group of the controlling terminal. Note, however, that
208 once a server has disassociated itself it can no longer
209 send reports of errors to a terminal, and must log errors
212 Once a server has established a pristine environment, it
213 creates a socket and begins accepting service requests.
214 The \fIbind\fP call is required to insure the server listens
215 at its expected location. It should be noted that the
216 remote login server listens at a restricted port number, and must
218 with a user-id of root.
219 This concept of a ``restricted port number'' is 4BSD
220 specific, and is covered in section 5.
222 The main body of the loop is fairly simple:
224 .if t .ta .5i 1.0i 1.5i 2.0i
225 .if n .ta .7i 1.4i 2.1i 2.8i
227 int g, len = sizeof (from);
229 g = accept(f, (struct sockaddr *)&from, &len);
232 syslog(LOG_ERR, "rlogind: accept: %m");
235 if (fork() == 0) { /* Child */
239 close(g); /* Parent */
242 An \fIaccept\fP call blocks the server until
243 a client requests service. This call could return a
244 failure status if the call is interrupted by a signal
245 such as SIGCHLD (to be discussed in section 5). Therefore,
246 the return value from \fIaccept\fP is checked to insure
247 a connection has actually been established, and
248 an error report is logged via \fIsyslog\fP if an error
252 in hand, the server then forks a child process and invokes
253 the main body of the remote login protocol processing. Note
254 how the socket used by the parent for queuing connection
255 requests is closed in the child, while the socket created as
256 a result of the \fIaccept\fP is closed in the parent. The
257 address of the client is also handed the \fIdoit\fP routine
258 because it requires it in authenticating clients.
262 The client side of the remote login service was shown
264 One can see the separate, asymmetric roles of the client
265 and server clearly in the code. The server is a passive entity,
266 listening for client connections, while the client process is
267 an active entity, initiating a connection when invoked.
269 Let us consider more closely the steps taken
270 by the client remote login process. As in the server process,
271 the first step is to locate the service definition for a remote
274 sp = getservbyname("login", "tcp");
276 fprintf(stderr, "rlogin: login/tcp: unknown service\en");
280 Next the destination host is looked up with a
281 \fIgethostbyname\fP call:
283 hp = gethostbyname(argv[1]);
285 fprintf(stderr, "rlogin: %s: unknown host\en", argv[1]);
289 With this accomplished, all that is required is to establish a
290 connection to the server at the requested host and start up the
291 remote login protocol. The address buffer is cleared, then filled
292 in with the Internet address of the foreign host and the port
293 number at which the login process resides on the foreign host:
295 bzero((char *)&server, sizeof (server));
296 bcopy(hp->h_addr, (char *) &server.sin_addr, hp->h_length);
297 server.sin_family = hp->h_addrtype;
298 server.sin_port = sp->s_port;
300 A socket is created, and a connection initiated. Note
301 that \fIconnect\fP implicitly performs a \fIbind\fP
302 call, since \fIs\fP is unbound.
304 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
306 perror("rlogin: socket");
310 if (connect(s, (struct sockaddr *) &server, sizeof (server)) < 0) {
311 perror("rlogin: connect");
315 The details of the remote login protocol will not be considered here.
317 Connectionless servers
319 While connection-based services are the norm, some services
320 are based on the use of datagram sockets. One, in particular,
321 is the \*(lqrwho\*(rq service which provides users with status
322 information for hosts connected to a local area
323 network. This service, while predicated on the ability to
324 \fIbroadcast\fP information to all hosts connected to a particular
325 network, is of interest as an example usage of datagram sockets.
327 A user on any machine running the rwho server may find out
328 the current status of a machine with the \fIruptime\fP(1) program.
329 The output generated is illustrated in Figure 3.
334 arpa up 9:45, 5 users, load 1.15, 1.39, 1.31
335 cad up 2+12:04, 8 users, load 4.67, 5.13, 4.59
336 calder up 10:10, 0 users, load 0.27, 0.15, 0.14
337 dali up 2+06:28, 9 users, load 1.04, 1.20, 1.65
338 degas up 25+09:48, 0 users, load 1.49, 1.43, 1.41
339 ear up 5+00:05, 0 users, load 1.51, 1.54, 1.56
343 kim up 3+09:16, 8 users, load 2.03, 2.46, 3.11
344 matisse up 3+06:18, 0 users, load 0.03, 0.03, 0.05
345 medea up 3+09:39, 2 users, load 0.35, 0.37, 0.50
347 miro up 1+07:20, 7 users, load 4.59, 3.28, 2.12
348 monet up 1+00:43, 2 users, load 0.22, 0.09, 0.07
350 statvax up 2+15:57, 3 users, load 1.52, 1.81, 1.86
351 ucbvax up 9:34, 2 users, load 6.08, 5.16, 3.28
355 Figure 3. ruptime output.
359 Status information for each host is periodically broadcast
360 by rwho server processes on each machine. The same server
361 process also receives the status information and uses it
362 to update a database. This database is then interpreted
363 to generate the status information for each host. Servers
364 operate autonomously, coupled only by the local network and
365 its broadcast capabilities.
367 Note that the use of broadcast for such a task is fairly inefficient,
368 as all hosts must process each message, whether or not using an rwho server.
369 Unless such a service is sufficiently universal and is frequently used,
370 the expense of periodic broadcasts outweighs the simplicity.
372 Multicasting is an alternative to broadcasting.
373 Setting up multicast sockets is described in Section 5.10.
375 The rwho server, in a simplified form, is pictured in Figure
376 4. There are two separate tasks performed by the server. The
377 first task is to act as a receiver of status information broadcast
378 by other hosts on the network. This job is carried out in the
379 main loop of the program. Packets received at the rwho port
380 are interrogated to insure they've been sent by another rwho
381 server process, then are time stamped with their arrival time
382 and used to update a file indicating the status of the host.
383 When a host has not been heard from for an extended period of
384 time, the database interpretation routines assume the host is
385 down and indicate such on the status reports. This algorithm
386 is prone to error as a server may be down while a host is actually
387 up, but serves our current needs.
390 .if t .ta .5i 1.0i 1.5i 2.0i
391 .if n .ta .7i 1.4i 2.1i 2.8i
395 sp = getservbyname("who", "udp");
396 net = getnetbyname("localnet");
397 sin.sin_addr = inet_makeaddr(INADDR_ANY, net);
398 sin.sin_port = sp->s_port;
400 s = socket(AF_INET, SOCK_DGRAM, 0);
403 if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
404 syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
407 bind(s, (struct sockaddr *) &sin, sizeof (sin));
409 signal(SIGALRM, onalrm);
413 int cc, whod, len = sizeof (from);
415 cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0,
416 (struct sockaddr *)&from, &len);
418 if (cc < 0 && errno != EINTR)
419 syslog(LOG_ERR, "rwhod: recv: %m");
422 if (from.sin_port != sp->s_port) {
423 syslog(LOG_ERR, "rwhod: %d: bad from port",
424 ntohs(from.sin_port));
428 if (!verify(wd.wd_hostname)) {
429 syslog(LOG_ERR, "rwhod: malformed host name from %x",
430 ntohl(from.sin_addr.s_addr));
433 (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
434 whod = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
436 (void) time(&wd.wd_recvtime);
437 (void) write(whod, (char *)&wd, cc);
443 Figure 4. rwho server.
447 The second task performed by the server is to supply information
448 regarding the status of its host. This involves periodically
449 acquiring system status information, packaging it up in a message
450 and broadcasting it on the local network for other rwho servers
451 to hear. The supply function is triggered by a timer and
452 runs off a signal. Locating the system status
453 information is somewhat involved, but uninteresting. Deciding
454 where to transmit the resultant packet
455 is somewhat problematical, however.
457 Status information must be broadcast on the local network.
458 For networks which do not support the notion of broadcast another
459 scheme must be used to simulate or
460 replace broadcasting. One possibility is to enumerate the
461 known neighbors (based on the status messages received
462 from other rwho servers). This, unfortunately,
463 requires some bootstrapping information,
464 for a server will have no idea what machines are its
465 neighbors until it receives status messages from them.
466 Therefore, if all machines on a net are freshly booted,
467 no machine will have any
468 known neighbors and thus never receive, or send, any status information.
469 This is the identical problem faced by the routing table management
470 process in propagating routing status information. The standard
471 solution, unsatisfactory as it may be, is to inform one or more servers
472 of known neighbors and request that they always communicate with
473 these neighbors. If each server has at least one neighbor supplied
474 to it, status information may then propagate through
475 a neighbor to hosts which
476 are not (possibly) directly neighbors. If the server is able to
477 support networks which provide a broadcast capability, as well as
478 those which do not, then networks with an
479 arbitrary topology may share status information*.
481 * One must, however, be concerned about \*(lqloops\*(rq.
482 That is, if a host is connected to multiple networks, it
483 will receive status information from itself. This can lead
484 to an endless, wasteful, exchange of information.
487 It is important that software operating in a distributed
488 environment not have any site-dependent information compiled into it.
489 This would require a separate copy of the server at each host and
490 make maintenance a severe headache. 4.4BSD attempts to isolate
491 host-specific information from applications by providing system
492 calls which return the necessary information*.
494 * An example of such a system call is the \fIgethostname\fP(2)
495 call which returns the host's \*(lqofficial\*(rq name.
497 A mechanism exists, in the form of an \fIioctl\fP call,
498 for finding the collection
499 of networks to which a host is directly connected.
500 Further, a local network broadcasting mechanism
501 has been implemented at the socket level.
502 Combining these two features allows a process
503 to broadcast on any directly connected local
504 network which supports the notion of broadcasting
505 in a site independent manner. This allows 4.4BSD
506 to solve the problem of deciding how to propagate
507 status information in the case of \fIrwho\fP, or
508 more generally in broadcasting:
509 Such status information is broadcast to connected
510 networks at the socket level, where the connected networks
511 have been obtained via the appropriate \fIioctl\fP
514 such broadcastings are complex, however, and will
515 be covered in section 5.