Fun with Client/Server Computing LG #33

Rate this post

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.

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.)

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.)

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 SSC, then use this article to start playing with more content.

After downloading the file 2333.tgz, expand it with the command tar&nsbp;-xzvf 2333.tgz. 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 gcc -oserver server.c; similarly compile client.c using gcc -oclient client.c.

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.

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

server 1024 &

and then start the client by typing

client hostname 1024

where hostname is the name or the IP address of your computer. If things work right, you will see output similar to the following:

Connection request from 192.168.1.1
14: Hello, World!

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 fg and ^C (ctrl-C).

Lire aussi...  Linux Gazette MailBag LG #34

Example of Query-Respone Client/Server

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:

/* local test of client-server code */

#include 
#include 
#include 

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

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

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

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

 client(buffer);

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

 client(buffer);

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

 client(buffer);

 return(0);
}

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.

Copy the above code into an editor and save it as localtest.c. Compile it with the command gcc -olocaltest localtest.c. When you run it you should get output like:

Please enter your name: joe
Hi, joe
Please enter your year of birth: 1960
Your approximate age is 38.
Enter q to quit: q

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:

int main(int argc, char *argv[])
{
int i, year, age;
char name[256] = "";
char buffer[256] = "";
char null_buffer[256] = "";
    int serverSocket = 0,

The application-specific code in server.c is towards the end. Replace it with the following:

/*
* 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