Adding QOS2 calls in a sample Winsock application: Part 1

Over the past few months we've been receiving feedback from partners on the new QOS2 API I just wrote about. The first question is often: how do I use this new API in my application?

So I thought we should introduce some of the API calls by modifying an existing code base. I decided to use a publicly available sample. You might recognize this code; it's from the simplec.c file for the Winsock sample in the Vista SDK. Since you can download the SDK and review the sample's code yourself, for the sake of clarity I've snipped out some of it. I also highlighted in yellow my additions to the code.

If you're wondering what the sample does, here's the cut & paste description from the source file. This is a non-adaptive scenario: no matter the available bandwidth, this client application is not adapting the number of UDP messages (or their size) it exchanges with the server.

Parse the command line and attempt to connect to the given server. The client will attempt to connect to each address returned by the getaddrinfo call until one succeeds afterwhich it will initiate echoing the data. Once the requested number of sends are issued, the connection is closed. For UDP, this is simply connecting the UDP socket to an endpoint and issuing the requested number of datagram sends and receives.

There are 3 QOS2 APIs used in my modifications:

  1. QOSCreateHandle is used to open the QOS2 subsystem.
    • The handle it returns is a file handle. You'll need this file handle in every QOS2 call you want to make. If you close this handle, all of the resources you've allocated using this handle get freed.
  2. QOSAddSocketToFlow is used to create a new flow with the to-be-connected socket.
    • The parameters are: the handle you obtained from QOSCreateHandle, a socket, optionally a destination address, a flow type and also optionally a flag. In exchange, it returns to you a flow id with which you can reference this new flow. In effect, I've created a new QOS2 flow and added the socket to it.
  3. QOSRemoveSocketFromFlow is the opposite of QOSAddSocketToFlow. I use it to clean up the QOS flow.
  4. QOSCloseHandle is the opposite of QOSCreateHandle. I use it to clean up the QOS subsystem.

That's it. With these modifications, all the traffic sent on the socket in this function will be marked with a DSCP value. I did skim over some things though. I'll write about them in the next few days.

P.S. You can download the Vista Beta 2 SDK from here.

- Mathias Jourdain

 

#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strsafe.h>

#include <qos2.h>

<Removed helper functions>

//
// Function: main
//
// Description:
//
int __cdecl main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET conn_socket = INVALID_SOCKET;
struct addrinfo *results = NULL,
*addrptr = NULL,
hints;
char *server_name = "localhost",
*port = DEFAULT_PORT,
Buffer[DEFAULT_BUFFER_LEN],
hoststr[NI_MAXHOST],
servstr[NI_MAXSERV];
int address_family = AF_UNSPEC,
socket_type = DEFAULT_PROTO;
int retval,
loopflag = 0,
loopcount,
maxloop = -1,
i;

QOS_VERSION version;
HANDLE QoSHandle = NULL;
BOOL QoSResult;
QOS_FLOWID QoSFlowID = 0;

    <Removed parameter parsing>

    // Load Winsock
if ((retval = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
{
fprintf(stderr,
"WSAStartup failed with error %d\n",
retval);
WSACleanup();
return -1;
}

    // Initialize the QoS version parameter
version.MajorVersion = 1;
version.MinorVersion = 0;
QoSResult = QOSCreateHandle(&version, &QoSHandle);

    if (QoSResult != TRUE){
fprintf(stderr,
"QOSCreateHandle failed to create"

                " handle with error %d\n",
GetLastError());

        goto cleanup;
}

    // Make sure the wildcard port wasn't specified
if (_strnicmp(port, "0", 1) == 0)
Usage(argv[0]);

    //
// Resolve the server name
//
memset(&hints, 0, sizeof(hints));
hints.ai_family = address_family;
hints.ai_socktype = socket_type;
hints.ai_protocol = ((socket_type == SOCK_STREAM)
? IPPROTO_TCP : IPPROTO_UDP);

    retval = getaddrinfo(
server_name,
port,
&hints,
&results
);
if (retval != 0)
{
fprintf(stderr, "getaddrinfo failed: %d\n", retval);
goto cleanup;
}

    // Make sure we got at least one address
if (results == NULL)
{
fprintf(stderr, "Server (%s) name could not be"
" resolved!\n", server_name);
goto cleanup;
}

    //
// Walk through the list of addresses returned
// and connect to each one.
//
// Take the first successful connection.
//
addrptr = results;
while (addrptr)
{
conn_socket = socket(addrptr->ai_family,
addrptr->ai_socktype,
addrptr->ai_protocol);

if (conn_socket == INVALID_SOCKET)
{
fprintf(stderr,
"socket failed: %d\n",
WSAGetLastError());
goto cleanup;
}

        retval = getnameinfo(
addrptr->ai_addr,
(socklen_t)addrptr->ai_addrlen,
hoststr,
NI_MAXHOST,
servstr,
NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (retval != 0)
{
fprintf(stderr,
"getnameinfo failed: %d\n",
retval);
goto cleanup;
}

        printf("Client attempting connection to:"
"%s port: %s\n", hoststr, servstr);

        QoSResult = QOSAddSocketToFlow(
                                 QoSHandle,
conn_socket,
addrptr->ai_addr,
QOSTrafficTypeExcellentEffort,
QOS_NON_ADAPTIVE_FLOW,
&QoSFlowID);
if (QoSResult != TRUE){
fprintf(stderr,
"QOSAddSocketToFlow failed to add"
" a flow with error %d\n",
GetLastError());
goto cleanup;
}

        retval = connect(conn_socket,
addrptr->ai_addr,
(int)addrptr->ai_addrlen);

if (retval == SOCKET_ERROR)
{

QOSRemoveSocketFromFlow(
QoSHandle,
NULL,
QoSFlowID,
0);
QoSFlowID = 0;

            closesocket(conn_socket);
conn_socket = INVALID_SOCKET;

            addrptr = addrptr->ai_next;
}
else
{
break;
}
}

    <Removed the code that sends the traffic>

    cleanup:

    //
// clean up the client connection
//

    if (conn_socket != INVALID_SOCKET)
{
// Indicate no more data to send
retval = shutdown(conn_socket, SD_SEND);
if (retval == SOCKET_ERROR)
{
fprintf(stderr,
"shutdown failed: %d\n",
WSAGetLastError());
}

        // Close the socket
retval = closesocket(conn_socket);
if (retval == SOCKET_ERROR)
{
fprintf(stderr,
"closesocket failed: %d\n",
WSAGetLastError());
}

        conn_socket = INVALID_SOCKET;
}

    if (QoSFlowID != 0)
{
QoSResult = QOSRemoveSocketFromFlow(QoSHandle,
NULL,
QoSFlowID,
0);

        if (QoSResult != TRUE){
fprintf(stderr,
"QOSRemoveSocketFromFlow failed"
" to end a flow with error %d\n",
GetLastError());
goto cleanup;
}
}

    if (QoSHandle != NULL)
{
QOSCloseHandle(QoSHandle);
}

    if (results != NULL)
{
freeaddrinfo(results);
results = NULL;
}

    WSACleanup();

    return 0;
}