Examples

Examples

In this section, we'll take a look at two programming examples of using QOS over TCP and UDP. The first example, which uses TCP, will demonstrate how to set up a FLOWSPEC and manage RSVP signaling on the Windows 98, Windows Me, and Windows 2000 platforms. The second example describes UDP and is primarily designed for Windows XP, which demonstrates only how to set up a FLOWSPEC to invoke the IP packet scheduler. The examples rely on a couple of support routines, PrintQos and FindProtocolInfo, which are defined in the files PRINTQOS.CPP and PROVIDER.CPP, respectively, both of which can be found on the companion CD. The former routine simply prints out the contents of a QOS structure, while the latter finds a protocol from the provider catalog with the required attributes, such as QOS.

TCP

The code for the TCP example can be found in a file on the companion CD called QOSTCP.CPP. The example is a bit long, but not particularly complicated. Most of the code is nothing more than the usual WSAEventSelect code that we introduced in Chapter 5. The only exception is what we do with an FD_QOS event. The main function doesn't do anything out of the ordinary. The arguments are parsed, a socket is created, and either the Server or the Client function is called—depending on whether the application is called as the server or the client. Let's examine the client connection first.

The sample has a command line parameter that tells the example to set QOS before connection, during connection, after connection, or after the peer requests QOS to set QOS locally. If QOS is selected to be set before connection (for the client), bind the socket to a random port and then call SIO_SET_QOS with a sending QOS FLOWSPEC. Note that it isn't really necessary to bind before calling SIO_SET_QOS because the peer's address is not known until a connect call is made, and an RSVP session cannot be initiated until the peer's address is known.

If the user elects to set QOS during connection, the example code passes the QOS structure into the WSAConnect call. The following code demonstrates how the sample optionally sets up QOS before or during the connection phase:

int  iSetQos; SOCKET s; char szServerAddr[32]; . . . const FLOWSPEC flowspec_notraffic = {QOS_NOT_SPECIFIED,                                      QOS_NOT_SPECIFIED,                                      QOS_NOT_SPECIFIED,                                      QOS_NOT_SPECIFIED,                                      QOS_NOT_SPECIFIED,                                      SERVICETYPE_NOTRAFFIC,                                      QOS_NOT_SPECIFIED,                                      QOS_NOT_SPECIFIED}; const FLOWSPEC flowspec_g711 = {8500,                                 680,                                 17000,                                 QOS_NOT_SPECIFIED,                                 QOS_NOT_SPECIFIED,                                 SERVICETYPE_CONTROLLEDLOAD,                                 340,                                 340}; QOS  clientQos;        // QOS client structure QOS  *lpqos; SOCKADDR_IN server, SOCKADDR_IN local; DWORD dwBytes; . . . // Set up the client's QOS flowspec      clientQos.SendingFlowspec = flowspec_g711; clientQos.ReceivingFlowspec =  flowspec_notraffic; clientQos.ProviderSpecific.buf = NULL; clientQos.ProviderSpecific.len = 0; if (iSetQos == SET_QOS_BEFORE) {    lpqos = NULL;    // Bind to the local interface and provide a flowspec    local.sin_family = AF_INET;    local.sin_port = htons(0);    local.sin_addr.s_addr = htonl(INADDR_ANY);             bind(s, (SOCKADDR *)&local, sizeof(local));    WSAIoctl(s, SIO_SET_QOS, &clientQos, sizeof(clientQos),              NULL, 0, &dwBytes, NULL, NULL); } else if (iSetQos == SET_QOS_DURING) {    // Use the QOS structure during connect    lpqos = &clientQos; } else if (iSetQos == SET_QOS_EVENT) {    lpqos = NULL;    // Set QOS later when signaled from a peer    clientQos.SendingFlowspec.ServiceType  = SERVICE_NO_QOS_SIGNALING;    clientQos.ReceivingFlowspec.ServiceType  = SERVICE_NO_QOS_SIGNALING;    WSAIoctl(s, SIO_SET_QOS, &clientQos, sizeof(clientQos), NULL, 0,              &dwBytes, NULL, NULL); } server.sin_family = AF_INET; server.sin_port = htons(5150); server.sin_addr.s_addr = inet_addr(szServerAddr); printf("Connecting to: %s\n", inet_ntoa(server.sin_addr)); WSAConnect(s, (SOCKADDR *)&server, sizeof(server), NULL, NULL,             lpqos, NULL); . . .

The WSAConnect call initiates an RSVP session and connects the client to the specified server. Otherwise, the user specifies that the sample should wait for the peer to set QOS, and no QOS structure is passed to WSAConnect. Instead, the code takes the sending QOS structure, ORs in the SERVICE_NO_QOS_SIGNALING flag to the ServiceType field in the FLOWSPEC structures, and calls WSAIoctl with the SIO_SET_FLAG ioctl command. This tells the QOS service provider not to invoke TC but to still look for RSVP messages.

After QOS is set, the events that the client wants to be notified of are registered, including FD_QOS. Notice that QOS must be set on the socket beforehand for the application to request receiving FD_QOS. Once this occurs, the client waits in a loop on WSAWaitForMultipleEvents, which unblocks when one of the selected events is signaled. Once an event occurs, the events are then enumerated along with any errors in WSAEnumNetworkEvents. The following code fragment demonstrates how the sample handles FD_QOS events through WSAEventSelect:

wbuf.buf = databuf; wbuf.len = DATA_BUFFER_SZ; memset(databuf, '#', DATA_BUFFER_SZ); databuf[DATA_BUFFER_SZ-1] = 0;   while (1) {    ret = WSAWaitForMultipleEvents(1, &hEvent, FALSE,              WSA_INFINITE, FALSE);    if (ret == WSA_WAIT_FAILED)    {       printf("WSAWaitForMulipleEvents() failed: %d\n",               WSAGetLastError());       return;    }    WSAEnumNetworkEvents(s, hEvent, &ne);    if (ne.lNetworkEvents & FD_READ)    {       // Read notification occurred    }    if (ne.lNetworkEvents & FD_WRITE)    {       if (ne.iErrorCode[FD_WRITE_BIT])          printf("FD_WRITE error: %d\n", ne.iErrorCode[FD_WRITE_BIT]);       else          printf("FD_WRITE\n");       if (!bWaitToSend)       {          wbuf.buf = databuf;          wbuf.len = DATA_BUFFER_SZ;          //          // If the network can't support the bandwidth don't send          //          if (!AbleToSend(s))          {             printf("Network is unable to provide "                    "sufficient best effort bandwidth\n");             printf("before the reservation "                    "request is approved\n");          }                                     WSASend(s, &wbuf, 1, &dwBytesSent, 0, NULL, NULL);          printf("Sent: %d bytes\n", dwBytesSent);       }    }        if (ne.lNetworkEvents & FD_CLOSE)    {       // Close notification occurred    }    if (ne.lNetworkEvents & FD_QOS)    {       char        buf[QOS_BUFFER_SZ];       QOS        *lpqos = NULL;       DWORD       dwBytes;       BOOL        bRecvRESV = FALSE;       if (ne.iErrorCode[FD_QOS_BIT])       {          printf("FD_QOS error: %d\n", ne.iErrorCode[FD_QOS_BIT]);          if (ne.iErrorCode[FD_QOS_BIT] == WSA_QOS_RECEIVERS)              bRecvRESV = TRUE;       }       else          printf("FD_QOS\n");       lpqos = (QOS *)buf;       WSAIoctl(s, SIO_GET_QOS, NULL, 0,                buf, QOS_BUFFER_SZ, &dwBytes, NULL, NULL);       //       // Check to see if there is a status object returned        // in the QOS structure which may also contain the       // WSA_QOS_RECEIVERS flag       //       if (ChkForQosStatus(lpqos, WSA_QOS_RECEIVERS))          bRecvRESV = TRUE;       if (iSetQos == SET_QOS_EVENT)       {          lpqos->SendingFlowspec.ServiceType =                      clientQos.SendingFlowspec.ServiceType;           WSAIoctl(s, SIO_SET_QOS, lpqos, dwBytes,                    NULL, 0, &dwBytes, NULL, NULL);                  //           // Change iSetQos so we don't set QOS again if we            // receive another FD_QOS event           //           iSetQos = SET_QOS_BEFORE;        }        if (bWaitToSend && bRecvRESV)        {           wbuf.buf = databuf;           wbuf.len = DATA_BUFFER_SZ;           WSASend(s, &wbuf, 1, &dwBytesSent, 0, NULL, NULL);           printf("Sent: %d bytes\n", dwBytesSent);        }     }  }

For the most part, QOSTCP.CPP handles the other events, such as FD_READ, FD_WRITE, and FD_CLOSE, the same way as the WSAEventSelect example code in Chapter 5. The only item of note is in the FD_WRITE event. One of the command line options is to wait until an RSVP PATH message has been received before sending the data. This is especially relevant if the data being transmitted is likely to exceed the best-effort bandwidth available on the network. The AbleToSend function calls SIO_CHK_QOS to determine if the QOS parameters requested are within the available best-effort limits. If so, it should be OK to start sending data; otherwise, wait for a confirmation to send data.

In our client's case, we want to receive the WSA_QOS_RECEIVERS message to indicate the receipt of an RESV message. This can be indicated upon receipt of an FD_QOS event. At this point, we call the SIO_CHK_QOS command to obtain status information. This WSA_QOS_RECEIVERS flag can be returned in two ways. First, the flag can be returned in the iErrorCode field of the WSANETWORKEVENTS structure as the element indexed by FD_QOS_BIT. Second, an RSVP_STATUS_INFO structure can be returned in the buffer passed to WSAIoctl using the SIO_GET_QOS ioctl command. This structure also might contain the WSA_QOS_RECEIVERS flag in its StatusCode field. If the wait to send flag has been specified, we check the error field from WSANETWORKEVENTS to see if an RSVP_STATUS_INFO structure has been returned. If the flag is present, we send data. That's all! The code necessary to support QOS for the client is straightforward.

The server side of the example is a bit more complicated, but only because it needs to manage zero or more client connections. The listening socket and the client sockets are handled in a single array, sc. Array element 0 is the listening socket and the rest are possible client connections. The global variable nConns contains the number of current clients. Whenever a client connection finishes, all active sockets are compacted toward the beginning of the socket array. There is also a corresponding array of event handles.

The server first binds the listening socket and sets receiving QOS if the user chooses to set QOS before accepting client connections. Any QOS parameters set on the listening socket are copied to the client connection (unless the server is using AcceptEx). The listening socket registers to receive only FD_ACCEPT events. The rest of the server routine is a loop that waits for events on the array of socket handles. At first, the only socket in the array is the listening socket, but as more client connections are established there will be more sockets and their corresponding events. If WSAWaitForMultipleEvents unblocks as a result of an event and indicates the event handle in array element 0, the event is occurring on the listening socket. If so, the code will call WSAEnumNetworkEvents to find out which event is occurring. If the event is occurring on a client socket, the code calls the handler routine HandleClientEvents.

On the listening socket, the event of interest is FD_ACCEPT. When this event happens, WSAAccept is called with a conditional function. Remember that the QOS parameters passed into the conditional function can't be trusted, and if the QOS parameter is non-null in Windows 98 and Windows Me, some sort of QOS must be set. Windows 2000 does not have that limitation—QOS can be set at any time. If the user specifies that QOS be set during the accept call, this occurs within the conditional function. Once the client socket is accepted, a corresponding event handle is created and the appropriate events are registered for the socket.

The function HandleClientEvents handles any events occurring on the client sockets. The read and write events are straightforward—the only exception is whether to wait for the reservation confirmation before sending. If the user specifies to wait for the reservation confirmation to arrive before sending data, the client waits for the WSA_QOS_RECEIVERS message to be returned in an FD_QOS event. If the message returns, the sending of the data doesn't occur until FD_QOS is received. Usually, the most significant aspect of this example is setting QOS on the socket and the FD_QOS handler.

UDP

The last sample provided, QOSUDP.CPP, demonstrates how to set up QOS over UDP and invoke the IP packet scheduler service without using RSVP signaling. This sample is written primarily for Windows XP (although it will work on other Windows platforms that support QOS) because RSVP signaling is no longer available.

The sample is just a sender that transmits datagrams to a specified receiving host that receives datagrams on port 5150. QOS is set up on the socket before datagrams are transmitted by calling SIO_SET_QOS with a QOS_DESTADDR object. An important part to note is the flag SERVICE_ NO_QOS_SIGNALING is ORed in with the ServiceType FLOWSPEC field so this application will behave the same on all Windows platforms regardless of whether RSVP signaling is available.



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