|
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.
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; } |