BSD 4_2 development
[unix-history] / usr / doc / ipc / 4.t
CommitLineData
f0b8d567
C
1.ds RH "Client/Server Model
2.bp
3.nr H1 4
4.nr H2 0
5.bp
6.LG
7.B
8.ce
94. CLIENT/SERVER MODEL
10.sp 2
11.R
12.NL
13.PP
14The most commonly used paradigm in constructing distributed applications
15is the client/server model. In this scheme client applications request
16services from a server process. This implies an asymmetry in establishing
17communication between the client and server which has been examined
18in section 2. In this section we will look more closely at the interactions
19between client and server, and consider some of the problems in developing
20client and server applications.
21.PP
22Client and server require a well known set of conventions before
23service may be rendered (and accepted). This set of conventions
24comprises a protocol which must be implemented at both ends of a
25connection. Depending on the situation, the protocol may be symmetric
26or asymmetric. In a symmetric protocol, either side may play the
27master or slave roles. In an asymmetric protocol, one side is
28immutably recognized as the master, with the other the slave.
29An example of a symmetric protocol is the TELNET protocol used in
30the Internet for remote terminal emulation. An example
31of an asymmetric protocol is the Internet file transfer protocol,
32FTP. No matter whether the specific protocol used in obtaining
33a service is symmetric or asymmetric, when accessing a service there
34is a \*(lqclient process\*(rq and a \*(lqserver process\*(rq. We
35will first consider the properties of server processes, then
36client processes.
37.PP
38A server process normally listens at a well know address for
39service requests. Alternative schemes which use a service server
40may be used to eliminate a flock of server processes clogging the
41system while remaining dormant most of the time. The Xerox
42Courier protocol uses the latter scheme. When using Courier, a
43Courier client process contacts a Courier server at the remote
44host and identifies the service it requires. The Courier server
45process then creates the appropriate server process based on a
46data base and \*(lqsplices\*(rq the client and server together,
47voiding its part in the transaction. This scheme is attractive
48in that the Courier server process may provide a single contact
49point for all services, as well as carrying out the initial steps
50in authentication. However, while this is an attractive possibility
51for standardizing access to services, it does introduce a certain
52amount of overhead due to the intermediate process involved.
53Implementations which provide this type of service within the
54system can minimize the cost of client server
55rendezvous. The \fIportal\fP notion described
56in the \*(lq4.2BSD System Manual\*(rq embodies many of the ideas
57found in Courier, with the rendezvous mechanism implemented internal
58to the system.
59.NH 2
60Servers
61.PP
62In 4.2BSD most servers are accessed at well known Internet addresses
63or UNIX domain names. When a server is started at boot time it
64advertises it services by listening at a well know location. For
65example, the remote login server's main loop is of the form shown
66in Figure 2.
67.KF
68.if t .ta .5i 1.0i 1.5i 2.0i
69.if n .ta .7i 1.4i 2.1i 2.8i
70.DS
71main(argc, argv)
72 int argc;
73 char **argv;
74{
75 int f;
76 struct sockaddr_in from;
77 struct servent *sp;
78
79 sp = getservbyname("login", "tcp");
80 if (sp == NULL) {
81 fprintf(stderr, "rlogind: tcp/login: unknown service\en");
82 exit(1);
83 }
84 ...
85#ifndef DEBUG
86 <<disassociate server from controlling terminal>>
87#endif
88 ...
89 sin.sin_port = sp->s_port;
90 ...
91 f = socket(AF_INET, SOCK_STREAM, 0);
92 ...
93 if (bind(f, (caddr_t)&sin, sizeof (sin)) < 0) {
94 ...
95 }
96 ...
97 listen(f, 5);
98 for (;;) {
99 int g, len = sizeof (from);
100
101 g = accept(f, &from, &len);
102 if (g < 0) {
103 if (errno != EINTR)
104 perror("rlogind: accept");
105 continue;
106 }
107 if (fork() == 0) {
108 close(f);
109 doit(g, &from);
110 }
111 close(g);
112 }
113}
114.DE
115.ce
116Figure 2. Remote login server.
117.sp
118.KE
119.PP
120The first step taken by the server is look up its service
121definition:
122.sp 1
123.nf
124.in +5
125.if t .ta .5i 1.0i 1.5i 2.0i
126.if n .ta .7i 1.4i 2.1i 2.8i
127sp = getservbyname("login", "tcp");
128if (sp == NULL) {
129 fprintf(stderr, "rlogind: tcp/login: unknown service\en");
130 exit(1);
131}
132.sp 1
133.in -5
134.fi
135This definition is used in later portions of the code to
136define the Internet port at which it listens for service
137requests (indicated by a connection).
138.PP
139Step two is to disassociate the server from the controlling
140terminal of its invoker. This is important as the server will
141likely not want to receive signals delivered to the process
142group of the controlling terminal.
143.PP
144Once a server has established a pristine environment, it
145creates a socket and begins accepting service requests.
146The \fIbind\fP call is required to insure the server listens
147at its expected location. The main body of the loop is
148fairly simple:
149.DS
150.if t .ta .5i 1.0i 1.5i 2.0i
151.if n .ta .7i 1.4i 2.1i 2.8i
152for (;;) {
153 int g, len = sizeof (from);
154
155 g = accept(f, &from, &len);
156 if (g < 0) {
157 if (errno != EINTR)
158 perror("rlogind: accept");
159 continue;
160 }
161 if (fork() == 0) {
162 close(f);
163 doit(g, &from);
164 }
165 close(g);
166}
167.DE
168An \fIaccept\fP call blocks the server until
169a client requests service. This call could return a
170failure status if the call is interrupted by a signal
171such as SIGCHLD (to be discussed in section 5). Therefore,
172the return value from \fIaccept\fP is checked to insure
173a connection has actually been established. With a connection
174in hand, the server then forks a child process and invokes
175the main body of the remote login protocol processing. Note
176how the socket used by the parent for queueing connection
177requests is closed in the child, while the socket created as
178a result of the accept is closed in the parent. The
179address of the client is also handed the \fIdoit\fP routine
180because it requires it in authenticating clients.
181.NH 2
182Clients
183.PP
184The client side of the remote login service was shown
185earlier in Figure 1.
186One can see the separate, asymmetric roles of the client
187and server clearly in the code. The server is a passive entity,
188listening for client connections, while the client process is
189an active entity, initiating a connection when invoked.
190.PP
191Let us consider more closely the steps taken
192by the client remote login process. As in the server process
193the first step is to locate the service definition for a remote
194login:
195.DS
196sp = getservbyname("login", "tcp");
197if (sp == NULL) {
198 fprintf(stderr, "rlogin: tcp/login: unknown service\en");
199 exit(1);
200}
201.DE
202Next the destination host is looked up with a
203\fIgethostbyname\fP call:
204.DS
205hp = gethostbyname(argv[1]);
206if (hp == NULL) {
207 fprintf(stderr, "rlogin: %s: unknown host\en", argv[1]);
208 exit(2);
209}
210.DE
211With this accomplished, all that is required is to establish a
212connection to the server at the requested host and start up the
213remote login protocol. The address buffer is cleared, then filled
214in with the Internet address of the foreign host and the port
215number at which the login process resides:
216.DS
217bzero((char *)&sin, sizeof (sin));
218bcopy(hp->h_addr, (char *)sin.sin_addr, hp->h_length);
219sin.sin_family = hp->h_addrtype;
220sin.sin_port = sp->s_port;
221.DE
222A socket is created, and a connection initiated.
223.DS
224s = socket(hp->h_addrtype, SOCK_STREAM, 0);
225if (s < 0) {
226 perror("rlogin: socket");
227 exit(3);
228}
229 ...
230if (connect(s, (char *)&sin, sizeof (sin)) < 0) {
231 perror("rlogin: connect");
232 exit(4);
233}
234.DE
235The details of the remote login protocol will not be considered here.
236.NH 2
237Connectionless servers
238.PP
239While connection-based services are the norm, some services
240are based on the use of datagram sockets. One, in particular,
241is the \*(lqrwho\*(rq service which provides users with status
242information for hosts connected to a local area
243network. This service, while predicated on the ability to
244\fIbroadcast\fP information to all hosts connected to a particular
245network, is of interest as an example usage of datagram sockets.
246.PP
247A user on any machine running the rwho server may find out
248the current status of a machine with the \fIruptime\fP(1) program.
249The output generated is illustrated in Figure 3.
250.KF
251.DS B
252.TS
253l r l l l l l.
254arpa up 9:45, 5 users, load 1.15, 1.39, 1.31
255cad up 2+12:04, 8 users, load 4.67, 5.13, 4.59
256calder up 10:10, 0 users, load 0.27, 0.15, 0.14
257dali up 2+06:28, 9 users, load 1.04, 1.20, 1.65
258degas up 25+09:48, 0 users, load 1.49, 1.43, 1.41
259ear up 5+00:05, 0 users, load 1.51, 1.54, 1.56
260ernie down 0:24
261esvax down 17:04
262ingres down 0:26
263kim up 3+09:16, 8 users, load 2.03, 2.46, 3.11
264matisse up 3+06:18, 0 users, load 0.03, 0.03, 0.05
265medea up 3+09:39, 2 users, load 0.35, 0.37, 0.50
266merlin down 19+15:37
267miro up 1+07:20, 7 users, load 4.59, 3.28, 2.12
268monet up 1+00:43, 2 users, load 0.22, 0.09, 0.07
269oz down 16:09
270statvax up 2+15:57, 3 users, load 1.52, 1.81, 1.86
271ucbvax up 9:34, 2 users, load 6.08, 5.16, 3.28
272.TE
273.DE
274.ce
275Figure 3. ruptime output.
276.sp
277.KE
278.PP
279Status information for each host is periodically broadcast
280by rwho server processes on each machine. The same server
281process also receives the status information and uses it
282to update a database. This database is then interpreted
283to generate the status information for each host. Servers
284operate autonomously, coupled only by the local network and
285its broadcast capabilities.
286.PP
287The rwho server, in a simplified form, is pictured in Figure
2884. There are two separate tasks performed by the server. The
289first task is to act as a receiver of status information broadcast
290by other hosts on the network. This job is carried out in the
291main loop of the program. Packets received at the rwho port
292are interrogated to insure they've been sent by another rwho
293server process, then are time stamped with their arrival time
294and used to update a file indicating the status of the host.
295When a host has not been heard from for an extended period of
296time, the database interpretation routines assume the host is
297down and indicate such on the status reports. This algorithm
298is prone to error as a server may be down while a host is actually
299up, but serves our current needs.
300.KF
301.DS
302.if t .ta .5i 1.0i 1.5i 2.0i
303.if n .ta .7i 1.4i 2.1i 2.8i
304main()
305{
306 ...
307 sp = getservbyname("who", "udp");
308 net = getnetbyname("localnet");
309 sin.sin_addr = inet_makeaddr(INADDR_ANY, net);
310 sin.sin_port = sp->s_port;
311 ...
312 s = socket(AF_INET, SOCK_DGRAM, 0);
313 ...
314 bind(s, &sin, sizeof (sin));
315 ...
316 sigset(SIGALRM, onalrm);
317 onalrm();
318 for (;;) {
319 struct whod wd;
320 int cc, whod, len = sizeof (from);
321
322 cc = recvfrom(s, (char *)&wd, sizeof (struct whod), 0, &from, &len);
323 if (cc <= 0) {
324 if (cc < 0 && errno != EINTR)
325 perror("rwhod: recv");
326 continue;
327 }
328 if (from.sin_port != sp->s_port) {
329 fprintf(stderr, "rwhod: %d: bad from port\en",
330 ntohs(from.sin_port));
331 continue;
332 }
333 ...
334 if (!verify(wd.wd_hostname)) {
335 fprintf(stderr, "rwhod: malformed host name from %x\en",
336 ntohl(from.sin_addr.s_addr));
337 continue;
338 }
339 (void) sprintf(path, "%s/whod.%s", RWHODIR, wd.wd_hostname);
340 whod = open(path, FWRONLY|FCREATE|FTRUNCATE, 0666);
341 ...
342 (void) time(&wd.wd_recvtime);
343 (void) write(whod, (char *)&wd, cc);
344 (void) close(whod);
345 }
346}
347.DE
348.ce
349Figure 4. rwho server.
350.KE
351.PP
352The second task performed by the server is to supply information
353regarding the status of its host. This involves periodically
354acquiring system status information, packaging it up in a message
355and broadcasting it on the local network for other rwho servers
356to hear. The supply function is triggered by a timer and
357runs off a signal. Locating the system status
358information is somewhat involved, but uninteresting. Deciding
359where to transmit the resultant packet does, however, indicates
360some problems with the current protocol.
361.PP
362Status information is broadcast on the local network.
363For networks which do not support the notion of broadcast another
364scheme must be used to simulate or
365replace broadcasting. One possibility is to enumerate the
366known neighbors (based on the status received). This, unfortunately,
367requires some bootstrapping information, as a
368server started up on a quiet network will have no
369known neighbors and thus never receive, or send, any status information.
370This is the identical problem faced by the routing table management
371process in propagating routing status information. The standard
372solution, unsatisfactory as it may be, is to inform one or more servers
373of known neighbors and request that they always communicate with
374these neighbors. If each server has at least one neighbor supplied
375it, status information may then propagate through
376a neighbor to hosts which
377are not (possibly) directly neighbors. If the server is able to
378support networks which provide a broadcast capability, as well as
379those which do not, then networks with an
380arbitrary topology may share status information*.
381.FS
382* One must, however, be concerned about \*(lqloops\*(rq.
383That is, if a host is connected to multiple networks, it
384will receive status information from itself. This can lead
385to an endless, wasteful, exchange of information.
386.FE
387.PP
388The second problem with the current scheme is that the rwho process
389services only a single local network, and this network is found by
390reading a file. It is important that software operating in a distributed
391environment not have any site-dependent information compiled into it.
392This would require a separate copy of the server at each host and
393make maintenance a severe headache. 4.2BSD attempts to isolate
394host-specific information from applications by providing system
395calls which return the necessary information\(dg.
396.FS
397\(dg An example of such a system call is the \fIgethostname\fP(2)
398call which returns the host's \*(lqofficial\*(rq name.
399.FE
400Unfortunately, no straightforward mechanism currently
401exists for finding the collection
402of networks to which a host is directly connected. Thus the
403rwho server performs a lookup in a file
404to find its local network. A better, though still
405unsatisfactory, scheme used by the routing process is to interrogate
406the system data structures to locate those directly connected
407networks. A mechanism to acquire this information from the system
408would be a useful addition.