Reliable Multicasting

Reliable Multicasting

The reliable multicasting provider is available on Windows XP when the Microsoft Message Queuing (MSMQ) component is installed. Reliable multicasting is a protocol built over IPv4 multicasting. Currently, it does not support IPv6. The reliable multicast implementation on Windows XP is based on the Pragmatic General Multicasting (PGM) specification. The protocol is NAK-based, meaning that all receivers of the data send notice to the sender only when it detects that a packet is missing. If the protocol was acknowledgment-based, like TCP, you would have cases in which hundreds or possibly thousands of receivers sent a packet to the sender indicating they successfully received a packet, which would flood the sender's network. This section provides only an overview of how the protocol is implemented so you can gain a basic understanding of how reliable multicasting is provided. For a complete specification of the PGM protocol, consult the IETF draft: draft-speakman-pgm-spec-06.txt.

The reliable multicast protocol works by the sender caching data sent to the receivers for a specified time period, which is known as the send window. This window is periodically moved forward, discarding the oldest data within the window to make room for new data the sender sends. There are three variables that make up the send window: the send rate, the size of the window (in bytes), and the size of the window in seconds (for example, how long the data remains in the window before being purged). The reliable multicast driver establishes certain default values for the window size that can be overridden (because the default values are rather low).

The protocol also contains a sequence number in the data packets so that if a receiver detects that it has missed a particular sequence, it may send a NAK to the sender, who will retransmit the missing data. Note that it is possible that a receiver NAKs for data that was recently purged from the send window. In this case, the receiver's “session” will be reset because of unrecoverable data loss—this is analogous to a TCP session being reset due to an ACK timeout or similar error.

In terms of the Winsock provider for reliable multicasting, there are a couple of important issues. First, there is a stream provider (SOCK_STREAM) as well as a reliable datagram provider (SOCK_RDM). The protocol value that is passed to the socket creation functions is IPPROTO_RM. The socket options specific to this protocol are contained in WSRM.H.

In the following sections we will look at how to create a reliable multicast sender as well as a receiver. A complete code sample is available on the companion CD in the directory IP-RM.

Reliable Sender

Establishing the reliable multicast sender is a simple process. The following list contains the necessary steps.

  1. Create a reliable multicast socket.

  2. Bind the socket to INADDR_ANY.

  3. Set the outgoing interface using RM_SET_SEND_IF.

  4. Connect the socket to the multicast group address that the data transmission is to take place on.

As with IP multicasting, it is necessary to set the outgoing interface in the case of a multihomed computer. Also note that no real connection is taking place between the sender and receiver. The connect that the sender called simply associates the destination multicast address with the socket. The following code example illustrates setting up a reliable multicast sender.

SOCKET s; ULONG sendif; SOCKADDR_IN localif, multi; char buf[1024]; int buflen=1024; s = socket(AF_INET, SOCK_RDM, IPPROTO_RM); // Bind to INADDR_ANY localif.sin_family = AF_INET; localif.sin_port   = htons(0); localif.sin_addr.s_addr = htonl(INADDR_ANY); bind(s, (SOCKADDR *)&localif, sizeof(localif)); // Set the outgoing interface sendif = inet_addr("157.124.22.104"); setsockopt(s, IPPROTO_RM, RM_SET_SEND_IF, (char *)&sendif,  sizeof(sendif)); // Connect the socket to the multicast destination multi.sin_family = AF_INET; multi.sin_port   = htons(5150); multi.sin_addr.s_addr = inet_addr("234.5.6.7"); connect(s, (SOCKADDR *)&multi, sizeof(multi)); // Send the data send(s, buf, buflen, 0); // Close up the session closesocket(s);

In this example, we create a socket of type SOCK_RDM. This is similar to SOCK_DGRAM because it provides message-oriented semantics but also implies reliability. Also, the local port supplied to the bind call is not significant. After the session is created, the sender may begin to send data.

Modifying the Window Size

By default, the reliable multicast driver sets up the default window size to buffer sent data. At some interval, the window is advanced by an increment, discarding the oldest data to make room for newer data. This window plays an important role in how reliable the data is. For example, a high data rate sender may receive many NAKs for lost data from receivers on a lower bandwidth or a congested network. As a result, if the send window is fairly small, those receivers may not be able to keep up. To prevent this, the sender may resize the send window to better suit the application's needs. This is done via the RM_RATE_WINDOW_SIZE socket option. The input parameter is a RM_SEND_WINDOW structure defined as

typedef struct _RM_SEND_WINDOW {     ULONG   RateKbitsPerSec;     ULONG   WindowSizeInMSecs;     ULONG   WindowSizeInBytes; } RM_SEND_WINDOW;

The RateKbitsPerSec specifies the data rate that the sender will be sending at. By default, the send rate is 56 Kbps. WindowSizeInMSecs specifies how long (in milliseconds) the data is to remain in the window before being purged to make room for new data. The last field, WindowSizeInBytes, represents how many bytes can be stored in the window before old data has to be purged to make room for new data. When setting this option, the following equation must hold true: WindowSizeInBytes = (RateKbitsPerSec/8) x WindowSizeInMSecs.

This option must be set before connecting the socket to the multicast destination address—all sender socket options must be set before issuing the connect call.

FEC

FEC is a method by which H parity packets are created out of K original data packets (for a total of N packets) so that the receiver can reconstruct the K original packets out of any K packets out of N received. For example, if for each four original data packets an additional four parity packets were created (for a total of eight packets), then the receiver needs to receive only any four of the eight to reconstruct the original data.

The algorithm employs packet-level Reed Solomon Erasure correcting techniques. This means that when FEC is enabled, more packets are hitting the wire as a number of parity packets are generated in addition to the data, but receivers typically lose a small number of packets at a time. If the receiver loses one packet out of the N generated, it can still recover the original data packets, thus preventing a NAK for the lost data and wait for the retransmission.

There are two operational modes in which FEC is enabled: pro-active and on-demand. With pro-active, the sender always computes parity packets for all data sent. The drawback is that computing parity packets can be a processor-intensive calculation. The second mode is on-demand, which means the sender sends data normally until a receiver sends a NAK, at which point the repair data is sent as a FEC data. In general, pro-active FEC is used when a large number of receivers are on lossy networks and the expected retransmission of lost data will become too large. On-demand is a trade off between reliability and network overhead. It is useful when several clients on the same network lose different packets belonging to the same FEC group. In this case, each receiver NAKs for the lost packet, at which point the sender will send a single FEC repair packet that will satisfy all of the clients' NAK request.

To enable FEC on a socket, the RM_USE_FEC socket option is used. This option must be set before connecting the socket. The input parameter is an RM_FEC_INFO structure that is defined as

typedef struct _RM_FEC_INFO {     USHORT              FECBlockSize;     USHORT              FECProActivePackets;     UCHAR               FECGroupSize;     BOOLEAN             fFECOnDemandParityEnabled; } RM_FEC_INFO;

The FECBlockSize field indicates the maximum number of packets generated. This field is the N packets discussed earlier (for example, original data packets plus parity packets). However, the current implementation ignores this field and instead relies on the sum of the FECProActivePackets and FECGroupSize fields to determine the block size. FECProActivePackets indicates the number of parity packets to be generated—this is the H parity packets generated. FECGroupSize is the number of original data packets used to generate parity information from—the original data packets, K. Finally, fFECOnDemandParityEnabled indicates whether on-demand FEC requests should be allowed.

Reliable Receiver

Setting up a reliable multicast receiver consists of the following five steps.

  1. Create a reliable multicast socket.

  2. Bind the socket specifying the multicast group address to join the session on.

  3. If the receiver wishes to listen on specific interfaces (as opposed to listening on all interfaces), call setsockopt and RM_ADD_RECEIVE_IF to add each interface.

  4. Call listen().

  5. Wait on an accept function.

There are a couple of important differences to note with the receiver. First, when binding the socket, the local interface is not specified. Instead, the multicast group the receiver wishes to join on is given. If the port number specified in the SOCKADDR_IN structure is zero, any reliable multicast session for the specified multicast group is accepted. Otherwise, if a specific port is given, then a reliable multicast session will be joined only if it matches the multicast group and the port number.

The second difference is that the receiver must add which interfaces to listen for incoming sessions on. Because the bind call is used to specify the multicast session to join, it is necessary to indicate which local interfaces should listen on for incoming sessions. By default, the socket will listen for incoming connections on any local interface. If the application wishes to accept sessions from a specific interface it must call RM_ADD_RECEIVE_IF for each local interface. Of course, this is important only on multihomed computers. The argument to the RM_ADD_RECEIVE_IF is the 32-bit network-byte order local interface to listen on. This option may be called multiple times. The option RM_DEL_RECEIVE_IF can be used to remove an interface from the listening set; however, it may be called only if RM_ADD_RECEIVE_IF has been invoked previously.

When a receiver joins a reliable multicast session, it does not have to start receiving at the beginning of the sender's data transmission. As a part of each session, the oldest data that may be requested for repair is advertised. Therefore, if the client joins the session after the sender has started transmitting data, the client may NAK for data back to the advertised sequence number. This is known as a late join. The sender can actually limit how much of its send window may be NAKed by late joiners (via the RM_LATEJOIN option). Of course, this is totally transparent to the application. The reliable multicast provider will automatically NAK for all available data when the session is joined. Once the session is joined, if data is lost and cannot be repaired, the session will be aborted. If, on the other hand, the sender closes the session, when all the remaining data has been delivered to the application the next receive operation will fail with the error WSEDISCONN.

The following code sample illustrates how to set up a reliable multicast receiver:

SOCKET             s,                    ns; SOCKADDR_IN        multi,                    safrom; ULONG              localif; char               buf[1024]; int                buflen=1024,                    fromlen,                    rc; s = socket(AF_INET, SOCK_RDM, IPPROTO_RM); multi.sin_family = AF_INET; multi.sin_port   = htons(5150); multi.sin_addr.s_addr = inet_addr("234.5.6.7"); bind(s, (SOCKADDR *)&multi, sizeof(multi)); listen(s, 10); localif = inet_addr("157.124.22.104"); setsockopt(s, IPPROTO_RM, RM_ADD_RECEIVE_IF,  (char *)&localif, sizeof(localif)); fromlen = sizeof(safrom); ns = accept(s, (SOCKADDR *)&safrom, &fromlen); closesocket(s);  // Don't need to listen anymore // start receiving data . . . while (1)  {     rc = recv(ns, buf, buflen, 0);         if (rc == SOCKET_ERROR) {         if (WSAGetLastError() == WSAEDISCON)             break;         else {             // An unexpected error         }     } } closesocket(ns);



Network Programming for Microsoft Windows
Network Programming for Microsoft Windows (Microsoft Professional Series)
ISBN: 0735605602
EAN: 2147483647
Year: 2001
Pages: 172
Authors: Anthony Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net