Our client takes up to two command-line arguments: the name of the daytime host to query and a port to connect to. By default, the program tries to contact a server running on the local host using the standard daytime service port number (13). Here's a sample session:
The client is different from the TCP-based programs we are more familiar with. Figure 18.1 shows the complete code for this program.
Lines 1 “5: Load modules We begin by turning on strict code checking and then bring in the standard Socket library and its line-end constants. We set $/ to CRLF , not because we'll be performing line-oriented reads, but in order for the chomp() call at the end of the script to remove the terminating CRLF properly.
Lines 6 “8: Define constants We define some constant values. DEFAULT_HOST is the name of the host to contact if not specified on the command line; we use the loopback address, "localhost." DEFAULT_PORT is the port to contact if not overridden on the command line; it can be either the port number or a symbolic service name. We use "daytime" as the service name.
UDP data is transmitted and received as discrete messages. MAX_MSG_LEN specifies the maximum size of a message. Since the daytime strings are only a few characters , it is safe to set this constant to a relatively small value of 100 bytes.
Lines 9 “10: Read command-line arguments We read the command-line arguments into the $host and $port global variables; if these variables are not provided, we use the defaults.
Lines 11 “13: Get protocol and port We use getprotobyname () to get the protocol number for UDP and call getservbyname() to look up the port number for the daytime service. If the user provided the port number directly, we skip the last step. We declare an empty variable named $data to receive the message transmitted by the remote host.
Line 14: Create the socket We create the socket by calling Perl's built-in socket() function. We use AF_INET for the domain, creating an Internet socket, SOCK_DGRAM for the type, creating a datagram-style socket, and the previously derived protocol number for UDP.
If successful, socket() returns a true value and assigns a socket to the filehandle. Otherwise, the call returns undef and we die with an error message.
Line 15: Create the destination address The final preparatory step is to create the destination address for outgoing messages. We call inet_aton() to turn the hostname into a packed string and pack this with the port into a sockaddr_in structure, using the function of the same name.
Line 16: Send the request We now have a socket and a destination address. The next step is to send a message to the server to tell it that it has a customer waiting. With the daytime service, one can send any message (even an empty one) and the server will respond with the time of day.
To send the message, we call the send() function. send() takes four arguments: the socket name, the message to send, the message flags, and the destination to send it to. For the message contents we use the string "What time is it?" but any string would do. We pass a 0 for the message flags in order to accept the defaults. For the destination address, we use the packed sockaddr_in address that we built earlier.
If the message is correctly queued for delivery, send() returns a true value. Otherwise, we die with an error message.
Line 17: Receive response The message has now been sent (or at least successfully queued), so we wait for a response using the recv() function. Like send() , this call also takes several arguments, including the socket, a variable in which to store the received data, and a numeric value indicating the maximum length of the message that we will receive.
If a message is received, recv() copies up to MAX_MSG_LEN bytes of it into $data . In case of an error, recv() returns undef , and we exit with an error message. Otherwise, recv() returns the packed address of the sender. We don't do anything with the sender's address but will put it to good use in the server examples given in later sections.
Lines 18 “19: Print the response We remove the CRLF at the end of the message with chomp() and print its contents to standard output.