|
|
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.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.