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