Previous Page Next Page

20.6. Client/Server Programs

The sample programs shown here were tested on Sun workstations running SunOS 4.1.3 and SunOS 5.3. These programs are examples of stream sockets for the UNIX and Internet domains. Although datagrams (UDP) are supported, they are unreliable and delivery of packets is not guaranteed. They are used in the Internet domain by programs such as rwho and ruptime but are rarely used in the UNIX domain.

20.6.1. Connection-Oriented Sockets on the Same Machine

The following scripts are very simple examples of the client/server model utilizing stream sockets for communication endpoints. Both client and server reside on the same machine, and the client simply reads a greetings message from the server and prints the message on the screen. There is little error checking or signal handling. The programs are simple demonstrations of how those socket functions are used. (For socket functions not included in this chapter, see Table A.1 in Appendix A.)

The Server Program
Example 20.17.

(The Script)
    #!/bin/perl

    # The server and the client are on the same machine.
    print "Server Started.\n";
1   $AF_UNIX=1;     # The domain is AF_UNIX
2   $SOCK_STREAM=1; # The type is SOCK_STREAM
3   $PROTOCOL=0;    # Protocol 0 is accepted as the "correct
                    # protocol" by most systems.

4   socket(SERVERSOCKET, $AF_UNIX, $SOCK_STREAM, $PROTOCOL) ||
        die " Socket $!\n";
    print "socket OK\n";
5   $name="./greetings";  # The name of the socket is associated
                          # within the file system
    unlink "./greetings" || warn "$name: $!\n";

6   bind(SERVERSOCKET, $name) || die "Bind $!\n";
    print "bind OK\n";

7   listen(SERVERSOCKET, 5) || die "Listen $!\n";
    print "listen OK\n";

    while(1){

8   accept(NEWSOCKET, SERVERSOCKET ) || die "Accept $!\n";
    # Accept client connection

9   $pid=fork || die "Fork: $!\n";
10  if ($pid == 0 ){
11      print (NEWSOCKET "Greetings from your server!!\n";

12      close(NEWSOCKET);
        exit(0);
    }
    else{
13      close (NEWSOCKET);
    }
  }

					  

Explanation

  1. The domain is set to UNIX. The client and server are on the same machine.

  2. The socket type is SOCK_STREAM, a connection-oriented, byte-stream type of communication.

  3. The protocol is set to 0. This value is handled by the system if set to 0.

  4. The socket function is called. The filehandle SERVERSOCKET is created in the server.

  5. The pathname of the file greetings is the name in the file system the socket SERVERSOCKET will be associated with. It will be the real name to which the socket filehandle is attached. If the file already exists, it will be removed with the unlink function.

  6. The socket filehandle is bound to the UNIX file greetings.

  7. The listen function allows the process to specify how many pending connections it will accept from the client. The requests are queued and the maximum number that can be queued is 5.

  8. The accept function waits for a client request and creates a new socket filehandle, NEWSOCKET, with the same attributes as the original filehandle, SERVERSOCKET (also called the rendezvous socket). NEWSOCKET is the socket that actually communicates with the client. The rendezvous socket remains available to accept future connections.

  9. A child process is created with the fork function. Now both parent and child are in execution.

  10. If the pid returned is zero, the child is in execution. If the pid is nonzero, the parent is in execution.

  11. If the child process is in execution (pid is zero), the greetings message is written to the socket NEWSOCKET. The server is communicating with its client.

  12. The NEWSOCKET filehandle is closed by the child and the child exits.

  13. The NEWSOCKET filehandle is closed by the parent so that it can receive more client requests.

The Client Program
Example 20.18.

(The Script)
    #!/usr/bin/perl
    print "Hi I'm the client\n";

1   $AF_UNIX=1;
2   $SOCK_STREAM=1;
3   $PROTOCOL=0;

4   socket(CLIENTSOCKET, $AF_UNIX, $SOCK_STREAM, $PROTOCOL);
5   $name="./greetings";

    do{
       # Client connects  with server
6      $result = connect(CLIENTSOCKET, "$name" );
       if ($result != 1 ){
          sleep(1);

       }
7  }while($result !=  1 ); # Loop until a connection is made

8  read(CLIENTSOCKET, $buf, 500);
9  print STDOUT "$buf\n";
10 close (CLIENTSOCKET);
   exit(0);

Explanation

  1. The domain is AF_UNIX. The client and server reside on the same machine. The value assigned to the scalar is the value that is assigned to the AF_UNIX macro in the system file /usr/include/sys/socket.h. The socket function requires these values as arguments in order to create a socket.

  2. The type of socket is SOCK_STREAM, a sequenced, reliable, bidirectional method of communication between the client and server. The value assigned to the scalar is the value that is assigned to the SOCK_STREAM macro in the system file /usr/include/sys/socket.h. The socket function requires these values as arguments in order to create a socket.

  3. The protocol value is handled by the system calls involved in creating the socket. It determines how the socket is implemented at a low level. By assigning 0 as the protocol value, the socket function considers this the "correct protocol"; that is, you don't have to worry about the details.

  4. The socket function creates a socket filehandle called CLIENTSOCKET.

  5. The socket filehandle CLIENTSOCKET will be associated with the UNIX file greetings.

  6. The connect function connects the CLIENTSOCKET filehandle to the UNIX file greetings so that the client can communicate with the server. The return from the function is true if it succeeded and false otherwise.

  7. Until the connection is made, the program loops.

  8. Now that the connection has been made, the client reads as many as 500 bytes from the server socket and stores the bytes in the scalar $buf.

  9. The contents of $buf are printed to STDOUT.

  10. The CLIENTSOCKET filehandle is closed. For more control, the shutdown function should be used.

 

Figure 20.2. The output.


20.6.2. Connection-Oriented Sockets on Remote Machines (Internet Clients and Servers)

The following program was executed on two Sun workstations. The server's hostname is scarecrow, running SunOS 5.3, and the client's hostname is houston, running SunOS 4.1.3. In this program, the client on one machine asks the server on another machine for the time. The server sends the time to the client socket, and the client prints the time in readable format to the screen. These examples do not take advantage of Perl 5's Socket.pm module in the standard library. All values are hardcoded and therefore are not necessarily portable from one machine to another. (See "The Socket.pm Module" on page 808.)

Example 20.19.

   #!/usr/bin/perl  -T
   # timeserver -- a Time Server program,
   # opens a Rendezvous Socket on port 9876
   # and waits for a client to connect.
   # When each client connects, this server determines the machine
   # time on its host and writes the value on the communication
   # socket to the client.
   #
   #                     Usage: timeserver [port number]
   #
   use strict;
   use warnings;
1  ($port)=@ARGV;
2  $port=9876 unless $port;
3  $AF_INET=2;
4  $SOCK_STREAM = 1;
5  $sockaddr = 'S n a4 x8';
6  ($name,$aliases,$proto)=getprotobyname('tcp');
7  if($port !~ /^\d+$/){
8      ($name, $aliases, $port)=getservbyport($port,'tcp');
   }
   print "Port = $port\n";
9  $this = pack($sockaddr, $AF_INET, $port, "\0\0\0\0");
10 select(COMM_SOCK); $| = 1; select (STDOUT);
       # Create R_SOCKET, the rendezvous socket descriptor
11 socket(R_SOCKET, $AF_INET, $SOCK_STREAM, $proto ) ||
          die "socket: $!\n";
       # Bind R_SOCKET to my address, $this
12 bind(R_SOCKET, $this) || die "bind: $!\n";
13 listen(R_SOCKET, 5) || die "connect: $!\n";
       # Infinite loop – wait until client connects,
       # then serve the client
   while(1){
14     accept(COMM_SOCK, R_SOCKET) || die "$!\n";
15     $now = time;
16     print COMM_SOCK $now;
   }

					  

Explanation

  1. A hostname may be passed as a command-line argument (see line 7).

  2. If the ARGV array is empty, the scalar $port is assigned the value 9876. This port number is assigned by the programmer to a number outside the reserved port numbers. On a Sun system, port numbers through 1024 are reserved. The port number 9876 is also called an ephemeral port; i.e., it is short-lived.

  3. The scalar $AF_INET is assigned the value 2, the constant value assigned to the macro AF_INET in /usr/include/sys/socket.h. This number represents the Internet domain, AF_INET.

  4. The type of socket is SOCK_STREAM, assigned the value of 1 in /usr/include/sys/socket.h.

  5. The pack function will use this format for the socket address.

  6. The getprotobyname function returns the official protocol name, any aliases, and the protocol number, using the tcp protocol as the name.

  7. If the scalar $port is not an assigned number, but the name of the server machine was passed in at the command line, the getservbyport function will return the correct port number for the server, using the tcp protocol.

  8. The port number is printed.

  9. The address for this Internet domain and port number is packed into a binary structure consisting of an unsigned short, a short in "network" order, four ASCII characters, and 8 null bytes. In comparable C programs, you will note that the method for getting addresses is by using a sockaddr (see line 5) structure (see/usr/include/sys/socket.h). Perl handles most of this for you.

  10. The socket filehandle is selected as the current default handle for output. The $| special variable is set to 1, forcing buffers to be flushed on every write or print. The stdout filehandle is normally line buffered when sending output to a terminal and block buffered otherwise. When output is going to a pipe or socket, the buffers will be flushed.

  11. The socket function creates the rendezvous socket filehandle R_SOCKET.

  12. The bind function binds the socket filehandle to the correct address for the server.

  13. The listen function sets the queue limit to 5, the maximum for pending requests from the client.

  14. The accept function waits for a client request, and when it gets one, accepts it by creating a new socket filehandle called COMM_SOCK with all the same attributes as R_SOCKET. COMM_SOCK is the server socket that will communicate with the client.

  15. The time function returns the number of non-leap-year seconds since Jan. 1, 1970, UTC.

  16. The time is sent to the socket filehandle COMM_SOCK.

 

Example 20.20.

   #!/usr/local/bin/perl
   # timeclient--a client for the Time Server program,
   # creates a socket and connects it to the server on port 9876.
   # The client then expects the server to write the server's
   # host time onto the socket. The client simply does
   # a read on its socket, SOCK, to get the server's time.
   #
   #          Usage: timeclient [server_host_name]
   #
   print "Hi, I'm in Perl program \'client\' \n";
1  ($them) = @ARGV;
2  $them = 'localhost' unless $them;
3  $port = 9876 ;      # timeserver is at this port number
4  $AF_INET = 2;
5  $SOCK_STREAM = 1;
6  $sockaddr = 'S n a4 x8';
7  ($name, $aliases, $proto) = getprotobyname('tcp');
8  ($name,$aliases, $port, $proto)=getservbyname($port, 'tcp')
       unless $port =~ /^\d+$/;

9  ($name,$aliases, $type, $len, $thataddr)=gethostbyname($them);
10 $that = pack($sockaddr, $AF_INET, $port, $thataddr);
   # Make the socket filehandle
11 if ( socket(SOCK, $AF_INET, $SOCK_STREAM, $proto ) ){
       print "Socket ok.\n";
   }
   else { die $!; }
   # Call up the server
12 if(connect(SOCK, $that)){
       print "Connect ok.\n";
   }
   else { die $!;}
   # Set socket to be command buffered
13 select(SOCK); $| = 1; select (STDOUT);
   # Now we're connected to the server, let's read her host time
14 $hertime = <SOCK>;
   close(SOCK);
   print "Server machine time is: $hertime\n";
15 @now = localtime($hertime);
   print "\t$now[2]:$now[1] ", $now[4]+1,"/$now[3]/$now[5]\n";

					  

Explanation

  1. The server's hostname may be passed as a command-line argument.

  2. If the ARGV array is empty, the hostname is set to localhost.

  3. To identify the server process, the client needs to know the server's port number.

  4. The domain is Internet.

  5. The type of socket is SOCK_STREAM, assigned the value of 1 in /usr/include/sys/socket.h.

  6. The pack function will use this format for the socket address.

  7. The getprotobyname function returns the official protocol name, any aliases, and the protocol number, using the tcp protocol as the name.

  8. The getservbyname function returns the name of the official name of the server, any aliases, the port number, and the protocol name, unless $port contains already assigned digits.

  9. The raw network address information is obtained from the host by gethostbyname.

  10. The address for the server's Internet domain and port number is packed into a binary structure consisting of an unsigned short, a short in "network" order, four ASCII characters, and 8 null bytes. In comparable C programs, you will note that the method for getting addresses is by using a sockaddr structure (see /usr/include/sys/socket.h).

  11. The socket function creates an Internet domain, connection-oriented socket filehandle, SOCK.

  12. The connect function connects the client's socket to the server's address.

  13. The SOCK filehandle is selected. Buffers will be flushed after prints and writes.

  14. Perl reads from the SOCK filehandle. The server's time is retrieved.

  15. The time is converted to local time and printed.

 

Figure 20.3. Connection-oriented sockets on remote machines.


Previous Page Next Page