< QOS Program Example | QOS Programming Main | Chap 11: The RAW Socket >


 

 

Generic Quality of Service (QOS) 10 Part 6

 

 

What do we have in this chapter 10 part 6?

  1. TCP (continue)

  2. UDP

  3. ATM and QOS

 

Copy the common files used in previous program example and paste them in this project folder. These steps quite weird because in .NET programming we can import all those files directly and those files are copied automatically into the current project.

 

Generic Quality of Service (QOS): Copying the header and its definition files.

 

Generic Quality of Service (QOS): Pasting the header and its definition files.

 

Then add those files into the existing project.

 

Generic Quality of Service (QOS): Adding the header and its definition files to the existing project.

 

 

Generic Quality of Service (QOS): Selecting the header and its definition files.

 

Those added files can be seen in the Solution Explorer.

 

Generic Quality of Service (QOS): The added header and its definition files seen in Solution explorer.

 

Before running this program, you may want to make sure the QoS RSVP and its’ dependent services are running on the tested machine.

 

Generic Quality of Service (QOS): Enabling the QoS RSVP service on the Windows XP Pro machine

 

Generic Quality of Service (QOS): Enabling the QoS RSVP dependent services on the Windows XP Pro machine

 

Build and run the project.

 

Generic Quality of Service (QOS): A sample tcp qos program output without any argument

 

Run the project as a server/receiver.

 

Generic Quality of Service (QOS): A sample tcp qos program output with  arguments in action as a server/receiver

 

Then run the same program as a client/sender.

 

Generic Quality of Service (QOS): A sample tcp qos program output with  arguments in action as a sender/client 1

 

Generic Quality of Service (QOS): A sample tcp qos program output with  arguments in action as a sender/client 2

 

The server/receiver screenshot is shown below for two client connections.

 

Generic Quality of Service (QOS): A sample tcp qos program output with  arguments in action as a sender/client when the communication was completed

 

The example is a bit long, but not particularly complicated. Most of the code is nothing more than the usual WSAEventSelect() code. 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, previous program example handles the other events, such as FD_READ, FD_WRITE, and FD_CLOSE, the same way as the WSAEventSelect() example code in presented in other chapters. 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 following program example demonstrates how to set up QOS over UDP and invoke the IP packet scheduler service without using RSVP signaling.

Create a new empty Win32 console mode application and add the project/solution name.

 

------------------------------------------------------------

Generic Quality of Service (QOS): Creating a new empty Win32 console mode application project for UDP QOS program example

 

Add the following source code.

 

// Description:

//    This is a udp qos sender program example

//

// Command line options: none

//

// Link to ws2_32.lib

#include <winsock2.h>

#include <windows.h>

#include <qos.h>

#include <qossp.h>

#include "provider.h"

#include <stdio.h>

 

#define QOS_BUFFER_SZ       16000 // Default buffer size for SIO_GET_QOS

#define DATA_BUFFER_SZ       2048 // Send/Recv buffer size

 

QOS  sendQos;

 

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_TC = {8000,

                              DATA_BUFFER_SZ,

                              17000,

                              QOS_NOT_SPECIFIED,

                              QOS_NOT_SPECIFIED,

                              SERVICETYPE_CONTROLLEDLOAD | SERVICE_NO_QOS_SIGNALING,

                              340,

                              340};

 

// Function: main

// Description:

void main(int argc, char **argv)

{

   WSADATA           wsd;

   WSAPROTOCOL_INFO *pinfo=NULL;

   SOCKET            s;

   SOCKADDR_IN       local;

   SOCKADDR_IN       receiver;

   WSABUF            wbuf;

   DWORD             dwBytes;

   QOS              *lpqos=NULL;

   char              sndbuf[DATA_BUFFER_SZ];

   QOS_DESTADDR      qosdest;

 

   if (argc < 2)

   {

      printf("Usage: %s <receiver IP address>\n", argv[0]);

      return;

   }

 

   if ((WSAStartup(MAKEWORD(2,2), &wsd)) != 0)

   {

      printf("Unable to load Winsock, error %d\n", WSAGetLastError());

      return;

   }

   else

       printf("WSAStartup() is OK!\n");

 

   pinfo = FindProtocolInfo(AF_INET, SOCK_DGRAM, IPPROTO_UDP, XP1_QOS_SUPPORTED);

   if (!pinfo)

   {

      printf("Unable to find suitable provider!\n");

      return;

   }

   else

      printf("Suitable provider found!\n");

 

   printf("Provider returned: %S\n", pinfo->szProtocol);

 

   if ((s = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,

       FROM_PROTOCOL_INFO, pinfo, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

   {

      printf("WSASocket() failed with error code %d\n", WSAGetLastError());

      return;

   }

   else

      printf("WSASocket() is OK!\n");

 

   // No matter when we decide to set QOS, the socket should be

   // bound locally (even if its to INADDR_ANY). This is required

   // if you're not using WSAConnect which does an implicit bind,

   // but we'll do it anyway for simplicity.

   local.sin_family = AF_INET;

   local.sin_port = 0;

   local.sin_addr.s_addr = htonl(INADDR_ANY);

 

   if (bind(s, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR)

   {

      printf("bind() failed with error code %d\n", WSAGetLastError());

      return;

   }

   else

      printf("bind() is fine!\n");

 

   receiver.sin_family = AF_INET;

   receiver.sin_port = htons(5150);

   receiver.sin_addr.s_addr = inet_addr(argv[1]);

 

   qosdest.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;

   qosdest.ObjectHdr.ObjectLength = sizeof(QOS_DESTADDR);

   qosdest.SocketAddress = (SOCKADDR *)&receiver;

   qosdest.SocketAddressLength = sizeof(receiver);

 

   sendQos.SendingFlowspec = flowspec_TC;

   sendQos.ReceivingFlowspec =  flowspec_notraffic;

   sendQos.ProviderSpecific.buf = NULL;

   sendQos.ProviderSpecific.len = 0;

 

   lpqos = &sendQos;

   lpqos->ProviderSpecific.buf = (char *)&qosdest;

   lpqos->ProviderSpecific.len = sizeof(qosdest);

 

   if (WSAIoctl(s, SIO_SET_QOS, lpqos, sizeof(QOS) + sizeof(QOS_DESTADDR),  NULL, 0, &dwBytes, NULL, NULL) == SOCKET_ERROR)

   {

      printf("WSAIoctl(SIO_SET_QOS) failed with error code %d\n", WSAGetLastError());

      return;

   }

   else

      printf("WSAIoctl(SIO_SET_QOS) is OK!\n");

 

   memset(sndbuf, '$', DATA_BUFFER_SZ);

   wbuf.buf = sndbuf;

   wbuf.len = DATA_BUFFER_SZ - 1;

 

   // Send data continuously to a receiver

   while (1)

   {

      if (WSASendTo(s, &wbuf, 1, &dwBytes, 0, (SOCKADDR *)&receiver, sizeof(receiver), NULL, NULL) == SOCKET_ERROR)

      {

         if (WSAGetLastError() == WSAEWOULDBLOCK)

            continue;

 

         printf("WSASendTo() failed with error code %d\n", WSAGetLastError());

         return;

      }

      else

         printf("WSASendTo() is OK!\n");

   }

 

    if(closesocket(s) == 0)

        printf("closesocket(s) is OK!\n");

    else

        printf("closesocket(s) failed with error code %d\n",WSAGetLastError());

 

    if(WSACleanup() == 0)

        printf("WSACleanup() is OK!\n");

    else

        printf("WSACleanup() failed with error code %d\n", WSAGetLastError());

   return;

}

 

Add the provider header and its definition files created in the previous program example. Firstly, copy both files, provider.h and provider.cpp from the previous project.

 

Generic Quality of Service (QOS) UDP: Copying the header and its definition files.

 

 

 

 

Paste them to the udpqosexample project.

 

Generic Quality of Service (QOS) UDP: Pasting the header and its definition files.

 

Then, add them into the project.

 

Generic Quality of Service (QOS) UDP: Adding the header and its definition files to the existing project.

 

Generic Quality of Service (QOS) UDP: Selecting the header and its definition files.

 

Both files should be visible in the solution explorer.

 

Generic Quality of Service (QOS) UDP: The added header and its definition files seen in Solution explorer.

 

Build and run the project.

 

Generic Quality of Service (QOS) UDP: A sample output using default arguments

 

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.

 

ATM and QOS

 

Windows 98 (with Service Pack 1), Windows Me, Windows 2000, and Windows XP support ATM programming from Winsock. QOS is natively available on an ATM network, which means that the network, application, and policy components that are necessary for QOS over IP are not required over ATM. This includes the Admission Control Service and the RSVP protocol. Instead, the ATM switch performs bandwidth allocations and prevents over-allocation of bandwidth.

In addition to the differences we've already mentioned, the Winsock API functions behave a bit differently with ATM QOS than they do with QOS over IP. The first major difference is that the QOS bandwidth request is handled as part of the connection request. This differs from QOS over IP in that the RSVP session is established separately from the connection. Also, if the bandwidth request is rejected under ATM, the connection will fail.

This leads to our next point: both of the native ATM providers are connection-oriented. As a result, you don't have the problem of setting QOS levels for a connectionless socket and then having to specify the endpoint for communication. The next major difference is that only one side sets the QOS parameters for a connection. If the client wants to set QOS on a connection, both the sending and receiving FLOWSPEC structures are set within the QOS structure passed to WSAConnect(). These values will then be applied to the connection, in contrast to QOS over IP, in which the sender requests certain QOS levels and the receiver then makes the reservation. In addition, the listening socket might have QOS set using WSAIoctl() and SIO_SET_QOS. These values will be applied to any incoming connections. This also means that QOS must be set during connection setup. You cannot set QOS on an established connection.

This leads us to our last point: once QOS is set for a connection, you cannot renegotiate it by calling WSAIoctl() and SIO_SET_QOS. When QOS is set on a connection, it remains until the connection is closed.

Keep in mind that RSVP is not present and no signaling occurs. None of the status flags in Table 10-5 are ever generated. QOS is set when establishing the connection, and no further notifications or events occur until the connection is closed.

 

 

 

Related reference:

 

MSDN QOS reference.

 


< QOS Program Example | QOS Programming Main | Chap 11: The RAW Socket >