< Enabling the Visual Studio Editor Line Numbers | Main | Managing HTTP Verbs & Authentication >

 


 

 

Chapter 14 Part 5:

Network Performance and Scalability

 

 

What do we have in this chapter 14 Part 5?

  1. A Simple C++/CLI Web Class Performance Measurement Program Example

A Simple C++/CLI Web Class Performance Measurement Program Example

 

Create a new CLR console application project. You may want to use the solution and project names as shown in the following screenshot.

 

A Simple C++/CLI Web Class Performance Measurement Program Example: creating new CLR console application

 

Add/edit the code as given in the following paragraph.

 

// FastGetCP.cpp : main project file.

//

// This sample measures HTTP GET performance through the HttpWebRequest class. Various

// options can be configured such as the connectionlimit, the total number of requests,

// Nagling, unsafe connection sharing, etc. The sample posts the requested number of

// asynchronous requests. When one completes, the response is retrieved, and an

// asynchronous stream receive is posted. The stream receive is reposted until the entire

// data stream has been read.

//

// Usage:

//      fastgetcp -c [int] -n [int] -u [URI] -p

//      -a              Allow unsafe authenticated connections

//      -c  int         Number of concurrent requests allowed

//      -d              Disable Nagle algorithm

//      -n  int         Total number of connections to make

//      -u  URI         URI resource to download

//      -p              Enable pipelining

//      -un string      User name

//      -up string      Password

//      -ud domain      Domain

//

// Sample usage:

//      fastgetcp -c 10 -n 10000 -u http://foo.com/default.html

//

//      fastgetcp -c 10 -n 10000 -u http://foo.com/default.html

//

#include "stdafx.h"

 

using namespace System;

using namespace System::IO;

using namespace System::Net;

using namespace System::Text;

using namespace System::Threading;

using namespace System::Globalization;

 

/// <summary>

/// Simple client designed to max out the network 

/// using HttpWebRequest.

/// </summary>

public ref class FastAsyncGetCP

{

public:

 

    static Uri^ uriName = nullptr;

    // need to re-instantiate an object for ResponseCallback crap, can't use nullptr

    // else you'll get

    // http://www.google.com/search?hl=en&q=Object+reference+not+set+to+an+instance+of+an+object&btnG=Search

    static ManualResetEvent^ allDone = gcnew ManualResetEvent(false);

    static int numRequests = 10;

    static int numConnections = 2;

    static int numRequestsCompleted = 0;

    // need to re-instantiate an object for ResponseCallback...

    // also do for other nullptr if needed

    static array<Byte>^readBuffer = gcnew array<Byte>(1200);

    static long totalBytesReceived = 0;

    static bool pipeliningEnabled = false;

    static bool useNagle = true;

    static bool unsafeAuthentication = false;

    // ...userInfo = gcnew NetworkCredential(userName, passWord, domain);

    static NetworkCredential^ userInfo = nullptr;

 

    /// <summary>

/// Displays usage information.

/// </summary>

static void usage()

{

            Console::WriteLine("In FastAsyncGetCP::usage()");

            Console::WriteLine("usage: fastgetcp -c [int] -n [int] -u [URI] -p");

            Console::WriteLine(" -a              Allow unsafe authenticated connections");

            Console::WriteLine(" -c  int         Number of concurrent requests allowed");

            Console::WriteLine(" -d              Disable Nagle algorithm");

            Console::WriteLine(" -n  int         Total number of connections to make");

            Console::WriteLine(" -u  URI         URI resource to download");

            Console::WriteLine(" -p              Enable pipelining");

            Console::WriteLine(" -un string      User name");

            Console::WriteLine(" -up string      Password");

            Console::WriteLine(" -ud domain      Domain");

            Console::WriteLine();

}

 

        /// <summary>

        /// Retrieve the given URI the requested number of times. This method initiates an asynchronous

        /// HTTP GET request for the URI.

        /// </summary>

        static void GetPages()

        {

            int i;

 

            Console::WriteLine("In FastAsyncGetCS::GetPages()");

            for (i = 0; i < numRequests; i++)

            {

                HttpWebRequest^ request = (HttpWebRequest^)WebRequest::Create(uriName);

 

                request->ServicePoint->ConnectionLimit = numConnections;

                request->ServicePoint->UseNagleAlgorithm = useNagle;

                request->UnsafeAuthenticatedConnectionSharing = unsafeAuthentication;

                request->Pipelined = pipeliningEnabled;

                request->Credentials = userInfo;

                if (userInfo != nullptr)

                        request->ConnectionGroupName = userInfo->UserName;

                request->BeginGetResponse(gcnew AsyncCallback(ResponseCallback), request);

            }

        }

 

                        /// <summary>

        /// This is the asynchronous callback invoked when the HTTP GET request completes. It retrieves

        /// the HTTP response object, obtains the data stream, and posts an asynchronous stream read

        /// to retrieve the data. Note that all receives for all requests use the same data buffer since

        /// we don't care about the data -- we just want to measure performance.

        /// </summary>

        /// <param name="result">Asynchronous context result for the operation</param>

private:

         static void ResponseCallback(IAsyncResult^ result)

        {

                // Get the RequestState object from the async result.

                HttpWebRequest^ reqstate = (HttpWebRequest^)result->AsyncState;

 

                Console::WriteLine("In FastAsyncGetCS::ResponseCallback()");

 

            try

            {

                // Retrieve the response and post a stream receive

                WebResponse^ response = reqstate->EndGetResponse(result);

                Stream^ responseStream = response->GetResponseStream();

                AsyncCallback^ acb = gcnew AsyncCallback(ReadCallBack);

                responseStream->BeginRead(readBuffer, 0, readBuffer->Length, acb, responseStream);

            }

            catch (Exception^ ex)

            {

                Console::WriteLine("Exception thrown in ResponseCallback... " + ex->ToString());

                reqstate->Abort();

                Interlocked::Increment(numRequestsCompleted);

            }

        }

 

        /// <summary>

        /// This is the asynchronous callback for the asynchronous stream receive operation posted after the

        /// HTTP response is obtained from a request. This method checks the number of bytes returned. If it is

        /// non-zero, another receive is posted as there could be more data pending. If zero is returned, we have

        /// read the entire data stream so we can close it.

        /// </summary>

        /// <param name="asyncResult">Asynchronous context result for the operation</param>

private:

        static void ReadCallBack(IAsyncResult^ asyncResult)

        {

            Stream^ responseStream = (Stream^)asyncResult->AsyncState;

            int read = responseStream->EndRead(asyncResult);

 

            Console::WriteLine("In FastAsyncGetCS::ReadCallBack()");

 

            if (read > 0)

            {

                // Possibly more data pending...post another receive

                totalBytesReceived += read;

                responseStream->BeginRead(readBuffer, 0, readBuffer->Length, gcnew AsyncCallback(ReadCallBack), responseStream);

            }

            else

            {

                // Reached the end of the stream so close it up

                responseStream->Close();

                Interlocked::Increment(numRequestsCompleted);

 

                if (numRequestsCompleted >= numRequests)

                {

                    allDone->Set();

                }

            }

        }

 

 

 

 

/// <summary>

/// This is the main method which parses the command line and initiates the GET requests.

/// It then waits for the 'allDone' method to be set at which point it calculates the performance

/// statistics and displays them to the console.

/// </summary>

/// <param name="args">Command line parameters</param>

public:

[STAThread]

int TestMain()

{

            Console::WriteLine("In FastAsyncGetCP::TestMain()");

            String^ userName = nullptr;

            String^ passWord = nullptr;

            String^ domain = nullptr;

 

            array<String^>^args = Environment::GetCommandLineArgs();

 

            for (int i = 0; i < args->Length; i++)

            {

                try

                {

                    if ((args[i][0] == '-') || (args[i][0] == '/'))

                    {

                            switch (Char::ToLower(args[i][1]))

                        {

                            case 'a':

                                // Allow unsafe authentication

                                unsafeAuthentication = true;

                                break;

                            case 'c':

                                // How many concurrent requests to allow

                                numConnections = Convert::ToInt32(args[++i]);

                                break;

                            case 'd':

                                // Disable Nagle algorithm

                                useNagle = false;

                                break;

                            case 'n':

                                // How many client connections to establish to server

                                numRequests = Convert::ToInt32(args[++i]);

                                break;

                            case 'p':

                                // Enable pipelining

                                pipeliningEnabled = true;

                                break;

                            case 'u':

                                    if (args[i]->Length == 2)

                                {

                                    // URI to retrieve

                                    uriName = gcnew Uri(args[++i]);

                                }

                                else

                                {

                                        switch (Char::ToLower(args[i][2]))

                                    {

                                        case 'n':

                                            // User name

                                            userName = args[++i];

                                            break;

                                        case 'p':

                                            // Password

                                            passWord = args[++i];

                                            break;

                                        case 'd':

                                            // Domain

                                            domain = args[++i];

                                            break;

                                        default:

                                            FastAsyncGetCP::usage();

                                            return 0;

                                    }

                                }

                                break;

                            default:

                                FastAsyncGetCP::usage();

                                return 0;

                        }

                    }

                }

                catch(Exception^ err)

                {

                    Console::WriteLine("Some bastard error lol: {0}", err->ToString());

                    FastAsyncGetCP::usage();

                    return 0;

                }

            }

 

            if (uriName == nullptr)

            {

                FastAsyncGetCP::usage();

                return 1;

            }

 

            if ((userName != nullptr) || (passWord != nullptr) || (domain != nullptr))

            {

                userInfo = gcnew NetworkCredential(userName, passWord, domain);

            }

 

            try

            {

                float start = 0.0;

                float end = 0.0;

                float total = 0.0;

 

                array<Byte>^ readBuffer = gcnew array<Byte>(1200);

                allDone = gcnew ManualResetEvent(false);

                // Gets the number of ms elapsed since the system started - start count

                start = (float)Environment::TickCount;

                Console::WriteLine("Counting start in ms: " + start);

 

                Console::WriteLine("Getting the pages...");

                FastAsyncGetCP::GetPages();

 

                allDone->WaitOne();

 

                // Gets the number of ms elapsed since the system started - end count

                end = (float)Environment::TickCount;

                Console::WriteLine("Counting end in ms: " + end);

                // total ms count from start to end

                total = end - start;

                Console::WriteLine("Total count for the elapsed time in ms: " + total);

 

                Console::WriteLine("Calculating the total KB/s...");

                long totalKBytes = (long)((totalBytesReceived / 1000) / (total / 1000));

 

                Console::WriteLine("Then total count in seconds " + total / 1000 + " seconds.");

                Console::WriteLine("Number of requests in the elapsed time: " + numRequests / (total / 1000) + " requests per second.");

                // Gets a NumberFormatInfo associated with the en-US culture.

                // Get the NumberFormatInfo object from the invariant culture.

                CultureInfo^ culture = gcnew CultureInfo("en-US");

                NumberFormatInfo^ nfi = culture->NumberFormat;

 

                nfi->NumberDecimalDigits = 0;

                Console::WriteLine("It is " + totalKBytes.ToString("N", nfi) + " KB per second.");

            }

            catch (Exception^ ex)

            {

                Console::WriteLine(ex->ToString());

                allDone->Set();

            }

    return 0;

}

};

 

int main(array<System::String ^> ^args)

{

    Console::WriteLine("In main()...");

    // instantiate an object...

    FastAsyncGetCP^ JustTest = gcnew FastAsyncGetCP();

    // call TestMain(), the real 'Main()'

    JustTest->TestMain();

    return 0;

}

 

 

 


< Enabling the Visual Studio Editor Line Numbers | Main | Managing HTTP Verbs & Authentication >