[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

The next step is to find the server's port number.  In the case of this
example, the port name is hardcoded into the server and client.  A real
networked application should have an entry for the "note" service in
the inet:db/services file.  The code below finds the port number in the
client machine's inet:db/services file.


    struct servent *servptr;
    char servnam[] = "note";

    if ((servptr = getservbyname( servnam, "tcp" )) == NULL)
    {
        printf("%s not in inet:db/services list!",servnam);
        FinalExit( RETURN_ERROR );
    }
    serv.sin_port = servptr->s_port;


    /*
    ** This tells the system the socket in question is an Internet socket
    */

    serv.sin_family = AF_INET;


Since the client and server are running on top of an IP (Internet
Protocol) system, the client needs to specify AF_INET in its call to
socket() just as the server did.

    /*
    ** Initialize the socket
    */

    if ( (sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 )
    {
        printf("socket gen: %s\n", strerror(errno));
        FinalExit( RETURN_ERROR );
    }


The client socket is initialized and ready.  Because the client knows
the IP address of the server's machine and the service number of the
"note" service, the client knows the well-known transport address so
it can attempt to establish a connection with the server:

    /*
    ** Connect the socket to the remote socket, which belongs to the
    ** server, and which will "wake up" the server.
    */

    if ( connect( sock,
                  (struct sockaddr *) &serv,
                  sizeof(struct sockaddr) ) < 0 )
    {
        printf("connect: %s\n", strerror(errno));
        s_close( sock );
        FinalExit( RETURN_ERROR );
    }


The connect() call contacts the server across the network and attempts
to form a connection.  There are many things which can go wrong at this
point, and any return less than zero indicates an error condition.  If
an error does occur, the socket.library function strerror() converts
the error number in errno into a readable error message, which is then
displayed by the client.  In case of an error, the client already has a
socket open, so it then must close the socket and terminate using the
application's error handler, FinalExit().

Once the connection is established, the client needs to prepare the
note request packet.  Since the call to ReadArgs() has already parsed
everything, the client only has to copy the strings into a NetNote
structure:

    struct NetNote out;

    out.nn_Code = NN_MSG;
    strcpy( (char *)&out.nn_Text, text );
    strcpy( (char *)&out.nn_Button, button );

The client has filled in all the relevant fields of the NetNote
structure, so it is ready to send.  The server will fill in the
nn_Retval field before passing it back to the client, so the client
doesn't need to fill it in for the client-to-server leg of the trip.
All that remains is to transfer the packet across the network:

    send( sock, (char *)&out, sizeof(struct NetNote), 0 );

    printf("\nMessage sent to %s...waiting for answer...\n", hostnam );

    /*
    ** Wait for either acknowlegde or error.
    */

    recv( sock, (char *)&out, sizeof(struct NetNote), 0 );


Now the client has to wait for the server to respond.  This is one of
the few points where the client can hang forever.  If the server
receives the message, and never replies back, the client will never
stop waiting for a reply.  A real application would time-out if the
server didn't respond within a certain time interval.  One way to do
this is using a selectwait() which breaks when triggered by a
timer.device event.  We'll leave that as an exercise.

The call to recv() waits until the server sends back a reply.  Once the
client receives the reply, the client has to check the NetNote
structure's nn_Code field.  If it's NN_ERR, an error occurred, and the
client terminates through the FinalExit() handler.  If an NN_ACK packet
comes back from the server, the nn_Retval field contains the number of
the button the user pressed on the ShowNote requester.

The server gets the button number directly from the EasyRequest()
function that the server uses to pop up the requester.  The buttons on
the requester are numbered from 1, increasing left to right, except for
the rightmost button, which is button zero.  If there is only one
button, that button will return a zero (it's the furthest right).

After the client gets the packet back from the server, the client's
task is complete.  It only has to clean up after itself.