What do we have in this chapter 10 part 3?
The programming techniques described in this section apply primarily to the Windows 98, Windows Me, and Windows 2000 platforms. As we mentioned, Windows XP does not feature a fully capable IP QOS service provider. Therefore, the Windows XP platforms no longer feature a supported RSVP signaling and admission control layer. The traffic control IP packet scheduler layer is still available and the technique of setting up a FLOWSPEC still applies, but information regarding QOS notifications and RSVP signaling does not apply.
Central to QOS is the initiation of an RSVP session. It's not until the RSVP PATH and RESV messages have been sent and processed that bandwidth is reserved for the process. Knowing when RSVP messages are generated is important to applications. For senders, three parameters must be known before a PATH message is generated:
The FLOWSPEC member is known whenever a QOS-enabled function is called, such as WSAConnect(), WSAJoinLeaf(), or WSAIoctl() (with the SIO_SET_QOS option). The source IP address and port will not be known until the socket is bound locally, either implicitly (such as by connecting) or explicitly by bind. Finally, the application needs the data's destination. This information is gathered either through a connect call or, in the case of connectionless UDP, by setting the QOS_DESTADDR object in the provider-specific data passed using the SIO_SET_QOS ioctl command. Similarly, for an RSVP RESV message to be generated, three things must be known:
The receiving FLOWSPEC member is obtained from any of the QOS-enabled Winsock functions. The address and port of each sender depend on the filter style, which can be set manually via the RSVP_RESERVE_INFO provider-specific structure, discussed earlier. Otherwise, this information is obtained from a PATH message. Of course, depending on the socket type, it is not always necessary to have already received a PATH statement to get the sender's address to generate RESV messages. The wildcard filter style used in multicasting is an example of this. The RESV message sent applies to all senders in the session. The local address and port are self-explanatory for unicast and UDP receivers but not for multicast receivers. With multicast receivers, the local address and port are the multicast address and its corresponding port number.
In this section, we'll cover the different socket types and their interaction with the QOS service provider and RSVP messages. Then we'll take a look at how the QOS service provider notifies applications of certain events. Understanding these concepts is central to writing successful QOS-enabled applications. Programming such applications is a matter of knowing how to obtain QOS guarantees as well as knowing when those guarantees are put into effect and when and how they can change.
You now have a basic understanding of how PATH and RESV RSVP messages are generated. In the following sections, we'll look at the different types of sockets - UDP, TCP, and multicast UDP, and how they interact with the QOS service provider to generate PATH and RESV messages.
Because you have the option of using either connected or unconnected UDP sockets, setting QOS on unicast UDP sockets presents quite a few options. With the UDP sender, the sending FLOWSPEC is obtained from one of the QOS-invoking functions. The local address and port are obtained either from an explicit bind call or from an implicit bind done by WSAConnect(). The last piece is the address and port of the receiving application, which can be specified either in WSAConnect() or via the QOS_DESTADDR provider-specific structure passed through the SIO_SET_QOS option. Be aware that if SIO_SET_QOS is used to set QOS, the socket must be bound beforehand.
For the UDP receiver, WSAConnect() can be called to limit the receiving application to a single sender. In addition, applications can specify a QOS_DESTADDR structure with the SIO_SET_QOS ioctl command. Otherwise, the SIO_SET_QOS can be called without providing any kind of destination address. In this case, an RESV message will be generated with the wildcard filter style. In fact, specifying the destination address via WSAConnect() or via the QOS_DESTADDR structure should be done only if you want the application to receive data from only one sender who uses the fixed filter style.
The UDP receiver can actually call both WSAConnect() and the SIO_SET_QOS ioctl command in any order. If SIO_SET_QOS is called before WSAConnect(), an RESV message is created with the wildcard filter first. Once the connect call is made, the previous RESV session is torn down and a new one is generated with the fixed filter style. Alternatively, calling SIO_SET_QOS after WSAConnect() and a fixed filter RESV message does not negate the RSVP session and generate a wildcard filter style. Instead, it simply updates the QOS parameters associated with the existing RSVP session.
TCP sessions have two possibilities. First, the sender can be the client who connects to the server and sends data. The second possibility is that the server that the client connects to might be the sender. With the client, QOS parameters can be specified directly in the WSAConnect() call, which will result in PATH messages being sent. The ioctl command SIO_SET_QOS can also be called before calling connect(), but until one of the connect() calls knows the destination address, no PATH messages will be generated.
When the sender is the server, the server calls WSAAccept() to accept the client connection. This function does not provide a means of setting QOS on the accepted socket. If QOS is set before a call to WSAAccept() by using SIO_SET_QOS, any accepted socket inherits the QOS levels set on the listening socket. Note that if the sender uses the conditional function in WSAAccept(), the function should pass QOS values set on the connecting client. However, this is not the case. The QOS service provider passes junk, which is the behavior in Windows 98, Windows Me, and Windows 2000. The exception is that if the lpSQOS parameter is non-null under Windows 98 and Windows Me, some kind of QOS values must be set via the SIO_SET_QOS ioctl command within the conditional function; otherwise, the WSAAccept() call fails even if CF_ACCEPT is returned. QOS can also be set on the client socket after it has been accepted.
Let's look at receiving TCP applications. The first case calls WSAConnect() with a receiving FLOWSPEC. When this occurs, the QOS service provider creates an RESV request. If QOS parameters are not supplied to WSAConnect(), the SIO_SET_QOS ioctl command can be set at a later time (resulting in an RESV message). The last combination is the server being the receiver, which is similar to the sending case. QOS can be set on the listening socket before a WSAAccept() call, in which case the client socket inherits the same QOS levels. Otherwise, QOS can be set in the conditional function or after the socket has been accepted. In either case, the QOS service provider generates an RESV message as soon as a PATH message arrives.
Multicast senders behave the same way as UDP senders except that WSAJoinLeaf() is used to become a member of the multicast group, as opposed to calling WSAConnect() with the destination address. QOS can be set with WSAJoinLeaf() or separately through an SIO_SET_QOS call. The multicast session address is used to compose the RSVP session object included in the RSVP PATH message.
With the multicast receiver, no RESV messages will be generated until the multicast address is specified via the WSAJoinLeaf() function. Because the multicast receiver doesn't specify a peer address, the QOS provider generates RESV messages with the wildcard filter style. The QOS service provider does not prohibit a socket from joining multiple multicast groups. In this case, the service provider sends RESV messages for all groups that have a matching PATH message. The QOS parameters supplied to each WSAJoinLeaf() will be used in each RESV message, but if SIO_SET_QOS is called on the socket after joining multiple groups, the new QOS parameters will be applied to all multicast groups joined.
When a sender sends data to a multicast group, only data sent to the multicast group that the sender joined results in QOS being applied to that data. In other words, if you join one multicast group and use sendto()/WSASendTo() with any other multicast group as the destination, QOS is not applied to that data. In addition, if a socket joins a multicast group specifying a particular direction (for example, using JL_SENDER_ONLY or JL_RECEIVER_ONLY in the dwFlags parameter to WSAJoinLeaf()), QOS is applied accordingly. A socket set as a receiver only will not gain any QOS benefits for sent data.
Thus far, you have learned how to invoke QOS for TCP, UDP, and multicast UDP sockets and the corresponding RSVP events that occur depending on whether you're sending or receiving. However, the completion of these RSVP messages is not strictly tied to the API calls that invoke them. That is, issuing a WSAConnect() call for a TCP receiving socket generates an RESV message, but the RESV message is independent of that API call because the call returns without any assurances that the reservation is approved and network resources are allocated. Because of this, a new asynchronous event has been added, FD_QOS, which is posted to a socket. Typically, an FD_QOS event notification will be posted in the following events:
To take advantage of these notifications, an application must register to be notified when an FD_QOS event occurs. You can do this in two different ways. First, you can use either WSAEventSelect() or WSAAsyncSelect() and include the FD_QOS flag in the bitwise ORing of event flags. However, an application is eligible to receive the FD_QOS event only if a call has already been made to one of the QOS–invoking functions. Note that in some cases an application might want to receive the FD_QOS event without having to set QOS levels on a socket. This can be accomplished by setting up a QOS structure whose sending and receiving FLOWSPEC members contain either the QOS_NOT_SPECIFIED or the SERVICETYPE_NOTRAFFIC flag. The only catch is that the SERVICE_NO_QOS_SIGNALING flag must be ORed with the SERVICETYPE_NOTRAFFIC flag for the direction of QOS in which you want to receive event notification.
If you use WSAEventSelect() once the event has been triggered, you should call the WSAEnumNetworkEvents() function to obtain additional status codes that might be available. Simply pass the socket handle, the event handle, and a WSANETWORKEVENTS object into the call, which will return and set event information into the supplied structure.
We mentioned earlier that there are a couple of ways to receive QOS notifications. This information actually ties into this section: obtaining the results of a QOS event. If you have registered to receive FD_QOS notifications with either WSAAsyncSelect() or WSAEventSelect() and you actually receive an FD_QOS event notification, you must perform a call to WSAIoctl() with the SIO_GET_QOS ioctl option to find out what triggered the event. You don't have to register for FD_QOS events, you can simply call WSAIoctl() with the SIO_GET_QOS command using overlapped I/O. This also requires that you specify a completion routine, which is invoked once the QOS service provider detects a change in QOS. Once the callback occurs, a QOS structure will be available in the output buffer.
In either case, once a change in QOS has occurred, your application can be notified of this change by registering for FD_QOS or by using overlapped I/O and SIO_GET_QOS. If you register for FD_QOS, call WSAIoctl() with the SIO_GET_QOS ioctl command upon event notification. For both methods, the QOS structure returned contains QOS information for only a single direction. That is, the FLOWSPEC structure for the invalid direction has its ServiceType field set to SERVICETYPE_NOCHANGE. In addition, more than one QOS event might have occurred, in which case you should call WSAIoctl() and SIO_GET_QOS in a loop until SOCKET_ERROR is returned and WSAGetLastError() returns WSAEWOULDBLOCK. The final concern when calling SIO_GET_QOS is the buffer size. When an FD_QOS event has been triggered, it is possible that provider-specific objects will be returned. In fact, the RSVP_STATUS_INFO structure will most often be returned, provided the buffer is large enough. See the earlier entry on WSAIoctl() for information about how to find the right-size buffer.
If your application uses one of the asynchronous event functions, a particularly important issue is that once an FD_QOS event occurs, you must always perform an SIO_GET_QOS operation to re-enable FD_QOS notifications.
You now know how to receive QOS event notifications and obtain new QOS parameters as a result of these events, but what types of notifications will occur? The first and most obvious reason for a QOS event is a change in the FLOWSPEC parameters for a given flow. For example, if you set up a socket with best-effort service, periodically, the QOS service provider will send notification to your application indicating the current conditions on the network. In addition, if you specify controlled load as well as other parameters, the QOS parameters for token bucket size and token rate might change slightly from what you requested once the reservation occurs. Your application should compare the FLOWSPEC returned once a QOS notification occurs to what you originally requested to ensure that it is sufficient for your application to continue. Also, remember that throughout the life of a QOS-enabled socket, you can always perform a SIO_SET_QOS to change any of the parameters, which will result in a QOS notification for the peer or peers associated with your current RSVP session. A robust application should be able to handle these conditions.
In addition to updating QOS parameters, QOS event notification signals other occurrences, such as notification of senders or receivers. The possible events are listed in Table 10-5. There are two ways to obtain these status codes. The first is as a part of the RSVP_STATUS_INFO object. When a QOS event occurs and a call is made to SIO_GET_QOS, it is possible that an RSVP_STATUS_INFO object will be returned as part of the provider-specific buffer. Second, if you use WSAEventSelect() to register for events, these codes can be returned in the WSANETWORKEVENTS structure returned from WSAEnumNetworkEvents(). The codes defined in Table 10-5 can be found in the iErrorCode array, indexed by FD_QOS_BIT. The first five codes listed are not error codes. They return valuable information concerning the status of the QOS connection. The other status codes listed in the table are QOS errors of concern, but they won't prevent you from sending and receiving data, they merely indicate an error in the QOS session. Of course, data sent in this situation will not carry any of the requested QOS guarantees.
With unicast, after a sender starts up and receives the first RESV message, a WSA_QOS_RECEIVERS is passed up to the application. If the receiver performs any steps to disable QOS, the result is an RESV teardown message. Once the sender receives this, WSA_QOS_NO_RECEIVERS is passed up to the application. Of course, with unicast many receivers simply close the socket, generating both an FD_CLOSE event and the WSA_QOS_NO_RECEIVERS event. In most cases, an application's response is simply to close the sending socket.
With multicast, the sending application receives WSA_QOS_RECEIVERS whenever the number of receivers changes and is nonzero. A single multicast sender receives WSA_QOS_RECEIVERS every time a QOS receiver joins the group, as well as every time a receiver drops out of a group, as long as at least one receiver remains.
The sender’s notification is similar to the receiver’s event except that it deals with the receipt of the PATH message. For unicast receivers after startup, the receipt of the first PATH message generates WSA_QOS_SENDERS, while the PATH teardown message initiates a WSA_QOS_NO_SENDERS message.
Likewise, multicast receivers receive the WSA_QOS_SENDERS notification whenever the number of senders decrements or increments and is nonzero. Once the number of senders reaches 0, the WSA_QOS_NO_SENDERS message is passed to the application.
This last status message is issued to receiving QOS applications if they ask to be notified when a reservation request has been confirmed. If it is set to nonzero, a field within the RSVP_STATUS_INFO structure named ConfirmRequest informs the QOS service provider to notify the application when the reservation request has been confirmed. This object is a provider-specific option that can be passed along with a QOS structure to the SIO_SET_QOS ioctl command.