| < Day Day Up > |
|
The multicast server and client are fundamentally derivatives of the datagram code patterns. This is primarily because multicast communication is based upon the datagram model.
The Ruby language source code for the multicast server is shown in Listing 20.6. Recall that multicast communication is group-based; therefore, a single message emitted by the server is received by every client that is currently a member of the multicast group.
As multicast communication has been discussed in previous chapters, we’ll forgo discussion of multicast specifics and concentrate solely on the methods that Ruby provides to achieve multicast communication. To set up our multicast server (lines 4 and 5), we first define the multicast group to which we belong and the port (in this case, “239.0.0.2”, 45002). Clients wanting to communicate with the server must also utilize this same group and port combination. Next, we create a typical datagram socket, at line 8, using the UDPSocket::new class method. We utilize the SO_REUSEADDR socket option (lines 11 and 12) in order to ensure that other clients (on the same host) can bind to this same group and port combination. Then at line 16, using the previously defined port number, we bind to the port number and Socket::INADDR_ANY (the wildcard interface).
Next, we enter the big loop, starting at line 22. Within this loop, we simply emit the current time at one second intervals to the multicast group (lines 25–26). Any clients having membership to this group and port will receive this time string. We emit the time string as would be done with any datagram server, specifying the string to send (Time::new:asctime), any flags, and, finally, the destination IP address and port (in this case, the multicast group and port number). The sleep method is used to wait for one second, and the process begins again by sending another time-string datagram. Finally, at line 34, we close the server socket using the close method.
Listing 20.6 Ruby Daytime multicast server.
1 require ‘socket’ 2 3 # Multicast Group and Port 4 mcast_group = "239.0.0.2" 5 group_port = 45002 6 7 # Create a new UDP Socket 8 servsock = UDPSocket::new(Socket::AF_INET) 9 10 # Make the address/port association immediately reusable 11 servsock.setsockopt( Socket::SOL_SOCKET, 12 Socket::SO_REUSEADDR, 1 ) 13 14 # Bind the server socket to the daytime port and 15 # any interface 16 servsock.bind( Socket::INADDR_ANY, group_port ) 17 18 # Debug data -- emit the server socket info 19 print("server address : ", servsock.addr::join(":"), "\n") 20 21 # The big loop 22 while true 23 24 # Emit the time through the socket to the client 25 servsock::send( Time::new::asctime+"\n", 0, 26 mcast_group, group_port ) 27 28 # Wait one second 29 sleep 1 30 31 end 32 33 # Close the server socket 34 servsock.close
The Ruby source code for the multicast client is shown in Listing 20.7. The multicast client includes a number of new features not previously discussed, but reflects symmetry with the multicast server, as seen with other code patterns.
After creating our UDP socket at line 8 (using the UDPSocket::new method), we make the address/port pair immediately reusable with the SO_REUSEADDR socket option (lines 11 and 12). At line 16, we use the bind method to permit our socket to accept datagrams from any port, and our previously defined multicast port (specified at line 5).
The next step is for the server to physically join the multicast group. This is performed using a socket option (IP_ADD_MEMBERSHIP), but before doing this, we must create a special address structure. The first step is creating a binary image of the IP address for which we want to join. Recall that our multicast group is “239.0.0.2”, so we create a binary array using the Array class and the pack method to create the binary equivalent (line 19). Lines 20 and 21 append another binary string representing the IP address of the current host. We first identify the host name for the host on which we’re executing using the Socket::gethostname method. This is used as an argument to Socket::gethostbyname, which communicates with the local resolver to resolve the host name to an IP address. Therefore, the variable mreq will represent a binary string containing eight bytes, four of the multicast group and four of the current host’s IP address. We then use this structure at lines 22 and 23 to actually join the multicast group using the setsockopt method.
Now that we’ve subscribed to the multicast group, we use the recv method at line 26 to receive a datagram from the group. Note that we use recv rather than recvfrom because we’re not interested in the source of the datagram (we know from whom it’s coming). We immediately emit the response datagram using the print method.
Finally, before we exit the client, we must remove ourselves from the multicast group. We use the setsockopt method again with IP_DROP_MEMBERSHIP to remove ourselves from the group (lines 29 and 30). The client socket is then closed using the close method (line 33).
Listing 20.7 Ruby Daytime multicast client.
1 require ‘socket’ 2 3 # Multicast Group and Port 4 mcast_group = "239.0.0.2" 5 group_port = 45002 6 7 # Create a new UDP Socket 8 clisock = UDPSocket::new(Socket::AF_INET) 9 10 # Make the address/port association immediately reusable 11 clisock.setsockopt( Socket::SOL_SOCKET, 12 Socket::SO_REUSEADDR, 1 ) 13 14 # Bind the client socket to the daytime port and 15 # any interface 16 clisock.bind( Socket::INADDR_ANY, group_port ) 17 18 # Join the multicast group 19 address = [239, 0, 0, 2].pack(‘CCCC’) 20 mreq = address + 21 Socket::gethostbyname(Socket::gethostname)[3] 22 clisock.setsockopt( Socket::IPPROTO_IP, 23 Socket::IP_ADD_MEMBERSHIP, mreq ) 24 25 # Await receipt of the time from the server 26 print clisock.recv(100) 27 28 # Leave the multicast group 29 clisock.setsockopt( Socket::IPPROTO_IP, 30 Socket::IP_DROP_MEMBERSHIP, mreq ) 31 32 # Close the client socket 33 clisock.close
| < Day Day Up > |
|