| < Day Day Up > |
|
The stream server and client demonstrate the Daytime protocol using stream sockets (reliable TCP-based communication).
The Daytime protocol stream server is shown in Listing 20.1. Because all networking applications must have access to the socket classes, we first make the socket classes visible by including the socket library using require (line 1). At line 2, we create our server socket using the TCPServer class. We bind our new server to all interfaces (as we’ve left the interface spec out) and the daytime port (which will resolve to 13). We could achieve the same result by specifying the host as “0.0.0.0”, which is synonymous with INADDR_ANY (from the C language perspective).
With our server socket defined in servsock, we emit some data for debugging purposes (to show the address of the server). The address printed will be “0.0.0.0”, as discussed previously. Next, we enter the infinite loop awaiting connections from external client sockets.
To accept a client connection, we use the accept instance method. This method returns a socket instance representing our connection to the client (line 13). We again emit some debugging information to show that a client successfully connected, and some information about it (lines 16 and 17).
In lines 20 and 21, we emit the time through the client socket. Note the use of Time::new, which returns a string representing the current time. We anonymously create the time string (returned by Time::new) and send it to the client using the write method. Because the time string contains no newline, we follow with another write (line 21) to ensure that the client receives a newline for proper printing. Because we’re now finished communicating with the client socket, we close it at line 24. This stops all communication with the client, except that any data remaining to be sent will be sent before the socket is physically closed.
Finally, at line 26, we terminate our while loop so processing begins again at line 13, awaiting a new client connection.
Listing 20.1 Ruby Daytime stream server.
1 require ‘socket’ 2 3 # Create a new TCP Server using port 13 4 servsock = TCPserver::new("daytime") 5 6 # Debug data -- emit the server socket info 7 print("server address : ", 8 servsock.addr::join(":"), "\n") 9 10 while true 11 12 # Await a connection from a client socket 13 clisock = servsock.accept 14 15 # Emit some debugging data on the peer 16 print("accepted ", clisock.peeraddr::join(":"), "\n") 17 print(clisock, " is accepted\n") 18 19 # Emit the time through the socket to the client 20 clisock.write( Time::new ) 21 clisock.write( "\n" ) 22 23 # Close the client connection 24 clisock.close 25 26 end
Before we move on to discuss the stream client, let’s discuss the method used to emit the debugging data at lines 8 and 16. The instance methods addr and peeraddr return information about the local socket and peer socket, respectively, in array form. The purposes of the join method are to create a new string by joining the elements together of the initial string and to separate them by the string argument to join (in this case, a ‘:’). This permits us to view the contents of the given array. For example, line 8 generates:
server address : AF_INET:13:0.0.0.0:0.0.0.0
The contents of the new array are “AF_INET” (the address family), 13 (the port), the resolved address (“0.0.0.0”, which is actually unresolved), and, finally, the IP address attached to the interface (“0.0.0.0”). Recall that because we specified no host in the creation of the TCP server socket, it was bound to the wildcard address so that connections could be accepted on any available interface (including localhost).
Now, let’s look at the Daytime stream client (shown in Listing 20.2). The client creates a socket to connect to the server and then awaits a string to be received through the socket. Upon receiving the string from the server, the message is printed and the client exits.
Listing 20.2 Ruby Daytime stream client.
1 require ‘socket’ 2 3 # Create a new client socket to the daytime port 4 mysock = TCPSocket::open("localhost", "daytime") 5 6 # Read a line from the socket 7 line = mysock.gets 8 9 # Print the line 10 print line 11 12 # Close the socket 13 mysock.close
We begin by making the socket library visible using the require method in line 1. Next, we create our stream using the TCPSocket class and the open method (line 4). We specify “localhost” as the host to connect, which is the loopback interface on the current host. We also specify the “daytime” port, which Ruby will resolve to 13. Recall that the TCPSocket::open method not only creates the client socket, but also connects it to the server as defined by the arguments. Therefore, when this method completes, we’ll be ready to communicate with the server.
To receive the time string from the server, we use the gets method with our previously created TCPsocket instance (mysock). The result of the gets method is a single line of input from the socket, which is stored in the line variable (line 7). We emit this string at line 10 using the print method and finally close the socket at line 13 using the close method.
Before we leave stream clients, let’s look at a simplified version of the Ruby stream client (shown in Listing 20.3). This client performs the socket creation followed by a gets method to retrieve the time string from the server. The entire line is preceded by the print method that emits the response from the socket methods.
Listing 20.3 Simplified Ruby Daytime stream client.
1 require ‘socket’ 2 3 # Short version -- open, read, close, and print in one line 4 print TCPSocket::open("localhost", "daytime")::gets
Scripts built with this style of method invocation are limited, but as is illustrated by Listing 20.3, can still be very useful.
| < Day Day Up > |
|