File: nelson.html

package info (click to toggle)
lg-issue33 3-4
  • links: PTS
  • area: main
  • in suites: woody
  • size: 2,284 kB
  • ctags: 137
  • sloc: makefile: 36; sh: 4
file content (378 lines) | stat: -rw-r--r-- 15,055 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
<!--startcut ==========================================================-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
   <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
   <META NAME="GENERATOR" CONTENT="Mozilla/4.05 [en] (Win95; U) [Netscape]">
   <TITLE>Fun with Client/Server Computing LG #33</TITLE>
</HEAD>
<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000FF" VLINK="#A000A0" ALINK="#FF0000">
<!--endcut ============================================================-->
<H4>
"Linux Gazette...<I>making Linux just a little more fun!</I>"</H4>

<HR>

<P><!--===================================================================-->
<CENTER>
<H1>
<FONT COLOR="#800000">Fun with Client/Server Computing</FONT></H1></CENTER>

<CENTER>
<H4>
By <A HREF="mailto:nelson@er.doe.gov">David Nelson</A></H4></CENTER>

<P> <HR> <P> 

Psst, wanna have some fun? Try client/server computing. It's like talking
through two tin cans and a taut string, upgraded to the computer era. Linux
has all the tools you need. You are already using client/server computing
in applications such as Netscape, telnet, and ftp. And it's easy to write
your own client/server apps, maybe even useful ones.

<P>Client/server computing links two different programs (the client and
the server) over a network. For practice you can even skip the network
by letting Linux talk to itself. So read on even if you aren't attached
to a network. (But your Linux installation needs to be configured for networking.)

<P>A very common form of client/server computing uses BSD sockets. BSD
stands for Berkeley Software Distribution, an early version of Unix. Logically,
a BSD socket is a combination of IP address and port number. The IP address
defines the computer, and the port number defines the logical communication
channel in that computer. (In this usage a port is not a physical device.
One physical device, e.g. an Ethernet card, can access all the ports in
the computer.)

<P>Linux Journal ran a nice three-part series on network programming by
Ivan Griffin and John Nelson in the February, March, and April, 1998, issues.
The February article contains the code to set up a skeleton client/server
pair using BSD sockets; it includes all the plumbing needed to get started.
You can download the code from <A HREF="ftp://ftp.ssc.com/pub/lj/listings/issue46/2333.tgz">SSC</A>,
then use this article to start playing with more content.

<P>After downloading the file 2333.tgz, expand it with the command 
<tt>tar&nsbp;-xzvf 2333.tgz</tt>. Rename the resultant file 2333l1.txt to server.c,
and the file 2333l2.txt to client.c. Edit server.c to delete the extraneous
characters @cx: from the start of the first line, and either delete the
last line or make it a comment by enclosing it between the characters /*
and */. Similarly, delete the last line of client.c, or make it a comment.
Compile server.c with the command <tt>gcc -oserver server.c</tt>; similarly
compile client.c using <tt>gcc -oclient client.c</tt>.

<P>The server runs on the local computer, so it only needs to know its
port number to define a socket. The client runs on any computer, so it
needs to know both its target server computer and the server's port number.
You have thousands of port numbers to play with. Just don't use a port
that is already taken. Your file /etc/services lists most of the ports
in use. I found that port 1024 worked fine.

<P>Now I said you didn't need to be connected to a network, but you do
need to have your computer configured for networking to try this out. In
fact, this code won't run for me if I use the generic name localhost; I
have to give the explicit name of my computer. So assuming you are set
up for networking, start the sever by typing
<PRE>server 1024 &amp;</PRE>
and then start the client by typing
<PRE>client hostname 1024</PRE>
where hostname is the name or the IP address of your computer. If things
work right, you will see output similar to the following:
<PRE>Connection request from 192.168.1.1
14: Hello, World!</PRE>
The first line gives the IP address of the client, and the second line
is the message from the server to the client. Considering all the code
involved, this would be a good entry for the World's Most Complex "Hello,
World" Program Contest! Note that the server keeps running in the background
until you kill it with the commands <tt>fg</tt> and <tt>^C</tt> (ctrl-C).
<H4>
Example of Query-Respone Client/Server</H4>
Now let's do something more useful. Debugging two programs simultaneously
is no fun, so let's start simple by simulating a client/server pair in
a single program. Then when you understand how things work we can divide
the code between the client and the server. In the following program the
client is simulated by the function client. The main routine simulates
the server:
<PRE>/* local test of client-server code */

#include &lt;stdio.h>
#include &lt;stdlib.h>
#include &lt;string.h>

char name[256] = "";
char buffer[256] = "";

void client(char *buffer)
{
&nbsp;printf("%s", buffer);
&nbsp;fgets(buffer, 256, stdin);
}

&nbsp;int main(int argc, char *argv[])
{
&nbsp;int year, age;

&nbsp;sprintf(buffer, "Please enter your name: ");

&nbsp;client(buffer);

&nbsp;strcpy(name, buffer);
&nbsp;sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);

&nbsp;client(buffer);

&nbsp;year = atoi(buffer);
&nbsp;age = 1998 - year;
&nbsp;sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);

&nbsp;client(buffer);

&nbsp;return(0);
}</PRE>
You don't have to be an expert at C code to see how this works. The simulated
server (main) sends the string "Please enter your name" to the simulated
client (client) through the array buffer. The client prints the string,
reads the name as a string from keyboard, and returns that string through
buffer. Then the server asks for the year of birth. When the client collects
it as a string, the server converts it to a number and subtracts it from
1998. It sends the resultant approximate age back to the client. We are
done now, but because the client needs a keyboard entry before returning,
the server requests that a "q" be entered. More sophisticated coding could
eliminate this unnecessary awkwardness. This simulated client/server illustrates
passing strings between server and client, asking and responding to questions,
and doing arithmetic.

<P>Copy the above code into an editor and save it as localtest.c. Compile
it with the command <tt>gcc -olocaltest localtest.c</tt>. When you run
it you should get output like:
<PRE>Please enter your name: joe
Hi, joe
Please enter your year of birth: 1960
Your approximate age is 38.
Enter q to quit: q</PRE>
Now let's turn this into a real client/server pair. Insert declarations
into server.c by changing the beginning statements of main to read:
<PRE>int main(int argc, char *argv[])
{
int i, year, age;
char name[256] = "";
char buffer[256] = "";
char null_buffer[256] = "";
&nbsp;&nbsp;&nbsp; int serverSocket = 0,</PRE>
The application-specific code in server.c is towards the end. Replace
it with the following:
<PRE>/*
* Server application specific code goes here,
* e.g. perform some action, respond to client etc.
*/

sprintf(buffer, "Please enter your name: ");
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i &lt;= 255; i++) buffer[i] = 0;

/* get name */
read(slaveSocket, buffer, sizeof(buffer));
strcpy(name, buffer);
sprintf(buffer, "Hi, %sPlease enter your year of birth: ", name);
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i &lt;= 255; i++) buffer[i] = 0;

/* get year of birth */
read(slaveSocket, buffer, sizeof(buffer));
year = atoi(buffer);
age = 1998 - year;
sprintf(buffer, "Your approximate age is %d.\nEnter q to quit: ", age);
write(slaveSocket, buffer, strlen(buffer));

close(slaveSocket);
exit(0);</PRE>
This is almost the same as the server code in the simulated client/server,
except that we read and write slaveSocket instead of calling the function
client. You can think of slaveSocket as the connection through the socket
between the server and client.

<P>The client code is very simple. Insert declarations into client.c
by changing the beginning statements of main to read
<PRE>int main(int argc, char *argv[])
{
&nbsp; int i;
&nbsp; int clientSocket,</PRE>
Find the application specific code near the end of client.c and replace
it with the following:
<PRE>/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
* Receive and respond until server stops sending messages
*/

while (0 &lt; (status = read(clientSocket, buffer, sizeof(buffer))))
&nbsp; {
&nbsp;&nbsp;&nbsp; printf("%s", buffer);
&nbsp;&nbsp;&nbsp; for (i = 0; i &lt;= 255; i++) buffer[i] = 0;
&nbsp;&nbsp;&nbsp; fgets(buffer, 256, stdin);
&nbsp;&nbsp;&nbsp; write(clientSocket, buffer, strlen(buffer));
&nbsp; }
&nbsp;&nbsp;&nbsp; close(clientSocket);
&nbsp;&nbsp;&nbsp; return 0;
&nbsp; }</PRE>
Again, this is almost the same as the client code in the simulated
client/server. The main differences are the use of clientSocket, the other
end of slaveSocket in the server, and the while statement for program control.
The while statement closes the client when the server stops sending messages.

<P>Recompile server.c and client.c and run them again as before. This
time the output should be something like:
<PRE>Connection request from 192.168.1.1
Please enter your name: joe
Hi, joe.
Please enter your year of birth: 1960
Your approximate age is 38.
Enter q to quit: q</PRE>
Now you can really play: try running multiple client sessions that
call the same server, and if you are on a network try running the server
on a different computer from the client. The server code is designed to
handle multiple simultaneous requests by starting a process for each client
session. This is done by the fork call in server.c. Read the man page for
fork to learn more.
<H4>
Chat Program as a Client/Server</H4>
As a final example, let's look at a chat program for sending messages
between users. It's primitive, because it only allows alternating lines
between each person, and it requires the server to keep a window open.
But it shows how a client/server pair can carry on an unlimited dialog,
and it could be extended into a practical program.

<P>Insert declarations into server.c by changing the beginning statements
of main to read:
<PRE>&nbsp;int main(int argc, char *argv[])
{
&nbsp; char buffer[256] = "";
&nbsp; int i, serverquit = 1, clientquit = 1;
&nbsp;&nbsp;&nbsp; int serverSocket = 0,</PRE>
Replace the application-specific code towards the end of server.c with
the following:
<PRE>/*
* Server application specific code goes here,
* e.g. perform some action, respond to client etc.
*/

printf("Send q to quit.\n");
sprintf(buffer, "Hi, %s\nS: Please start chat. Send q to quit.\n", inet_ntoa(clientName.sin_addr));
write(slaveSocket, buffer, strlen(buffer));
for (i = 0; i &lt;= 255; i++) buffer[i] = 0;

while (serverquit != 0 &amp;&amp; clientquit != 0)
{
&nbsp;status = 0;
&nbsp;while (status == 0)
&nbsp; status = read(slaveSocket, buffer, sizeof(buffer));
&nbsp;clientquit = strcmp(buffer, "q\n");

&nbsp;if (clientquit != 0)
&nbsp;{
&nbsp; printf("C: %s", buffer);
&nbsp; for (i = 0; i &lt;= 255; i++) buffer[i] = 0;

&nbsp; printf("S: ");
&nbsp; fgets(buffer, 256, stdin);
&nbsp; serverquit&nbsp; = strcmp(buffer, "q\n");
&nbsp; write(slaveSocket, buffer, strlen(buffer));
&nbsp; for (i = 0; i &lt;= 255; i++) buffer[i] = 0;
&nbsp;}
}
printf("Goodbye\n");
close(slaveSocket);
exit(0);</PRE>
Insert declarations into client.c by changing the beginning statements
of main to read:
<PRE>int main(int argc, char *argv[])
{
&nbsp;int i, serverquit = 1, clientquit = 1;
&nbsp;&nbsp;&nbsp; int clientSocket,</PRE>
Replace the application-specific code toward the end of client.c with
the following
<PRE>/*
* Client application specific code goes here
* e.g. receive messages from server, respond, etc.
*/

while (serverquit != 0 &amp;&amp; clientquit != 0)
{
&nbsp; status = 0;
&nbsp; while (status == 0)
&nbsp;&nbsp;&nbsp; status = read(clientSocket, buffer, sizeof(buffer));
&nbsp; serverquit = strcmp(buffer, "q\n");

&nbsp; if (serverquit != 0)
&nbsp; {
&nbsp;&nbsp;&nbsp; printf("S: %s", buffer);
&nbsp;&nbsp;&nbsp; for (i = 0; i &lt;= 255; i++) buffer[i] = 0;

&nbsp;&nbsp;&nbsp; printf("C: ");
&nbsp;&nbsp;&nbsp; fgets(buffer, 256, stdin);
&nbsp;&nbsp;&nbsp; clientquit = strcmp(buffer, "q\n");
&nbsp;&nbsp;&nbsp; write(clientSocket, buffer, strlen(buffer));
&nbsp;&nbsp;&nbsp; for (i = 0; i &lt;= 255; i++) buffer[i] = 0;
&nbsp;&nbsp; }
&nbsp;}
&nbsp;printf("Goodbye\n");
&nbsp;close(clientSocket);
&nbsp;return 0;
&nbsp;}</PRE>
Recompile both server.c and client.c and you are ready to try it out.
To simulate two computers on one, open two windows in X or use two different
consoles (e.g. alt-1 and alt-2.) Start the server in one window using the
command
<PRE>server 1024</PRE>
and the client in the other using the command
<PRE>client hostname 1024</PRE>
where hostname is replaced by your actual hostname or IP address.

<P>Server and client code for this chat program are almost identical,
and very similar to the previous example. There are two main differences.
The first is the test to see whether either party has entered a "q" to
quit. The flags serverquit and clientquit signal this. The second is the
tight loop waiting for response from the other party. The function read
returns the number of character read from the socket; this is stored into
status. A non-zero number of characters indicates the other side has sent
a message.

<P>Here is an example session as printed by the server:
<PRE>Connection request from 192.168.1.1
Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
Goodbye</PRE>
And here is the same session as printed by the client:
<PRE>S: Hi, 192.168.1.1
S: Please start chat. Send q to quit.
C: Hi server
S: Hi client
C: Bye server
S: Bye client
C: q
Goodbye</PRE>
I hope these examples have shown how easy it is to set up client/server
computing. May your appetite be whetted to try your own applications. If
you cook up something tasty, let the rest of us know. And don't forget
to keep that string taut!

<!--===================================================================-->
<P> <hr> <P> 
<center><H5>Copyright &copy; 1998, David Nelson <BR> 
Published in Issue 33 of <i>Linux Gazette</i>, October 1998</H5></center>

<!--===================================================================-->
<P> <hr> <P> 
<A HREF="./lg_toc33.html"><IMG ALIGN=BOTTOM SRC="../gx/indexnew.gif" 
ALT="[ TABLE OF CONTENTS ]"></A>
<A HREF="../lg_frontpage.html"><IMG ALIGN=BOTTOM SRC="../gx/homenew.gif"
ALT="[ FRONT PAGE ]"></A>
<A HREF="./kacur.html"><IMG SRC="../gx/back2.gif"
ALT=" Back "></A>
<A HREF="./burtch.html"><IMG SRC="../gx/fwd.gif" ALT=" Next "></A>
<P> <hr> <P> 
<!--startcut ==========================================================-->
</BODY>
</HTML>
<!--endcut ============================================================-->