|
This file implements the multi threaded version of the utility (Windows only). By default, under Windows) you are
limited to 64 threads.
// -------------------------------------------------------------
// Test program for the class "Http".
//
// Note: Do NOT try to use the following syntax to declare the
// object "http":
//
// Http http();
//
// If you do that, the compiler thinks that you decale a func-
// -tion that takes no argument and returns a object "Http".
// Therfore you get a message related to "non-aggregate" type.
//
// How to compile the program ?
//
// Do not forget the compiler's options:
// - /MT (for multithreading)
// - /GX (for exceptions handling
//
// Also you should use the library "ws2_32.lib" instead of
// "winsowk.lib".
// -------------------------------------------------------------
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include "http.hpp"
// -------------------------------------------------------------
// Global variables shared by all threads (read only)
// -------------------------------------------------------------
int port; // port used to connect to the HTTP server
int timeout; // connection timeout
char* hostname; // HTTP server's hostname
char* url; // Path to the web document
// -------------------------------------------------------------
// Print program's usage
// -------------------------------------------------------------
void print_usage()
{
cerr << endl
<< "Usage: http host_address path_to_document port timeout nb_threads"
<< endl
<< endl;
}
// -------------------------------------------------------------
// HTTP thread
// -------------------------------------------------------------
#define THREAD_OK 0 // success
#define THREAD_MEM_ERROR -1 // Memory allocation error
#define THREAD_INVALID_ARGS -2 // Invalid cpnnection's parameters
#define THREAD_CREATE_SOCK_ERR -3 // Can not create sockets
#define THREAD_CONNECT_FAILED -4 // Cnnect failed
#define THREAD_RESOLV_FAILED -5 // Can ot revolv hostname
#define THREAD_DLL_ERR -6 // DLL error
#define THREAD_DLL_VERSION -7 // Wrong DLL version
#define THREAD_NON_BLOCK_ERR -8 // Can ot set socket to non blocking mode
#define THREAD_BLOCK_ERROR -9 // Can ot set socket to blocking mode
#define THREAD_UNKNOWN_ERR -10 // Unknown error
#define THREAD_WRITE_ERR -11 // Can not write into socket
#define THREAD_READ_ERR -12 // Can not read from socket
#define THREAD_CONNECT_TIMEOUT -13 // Connect timeout
#define THREAD_READ_TIMEOUT -14 // Read timeout
#define THREAD_TIMER_ERR -15 // Can not create timer
// -------------------------------------------------------------
//
DWORD WINAPI Thread_HTTP_Proc(LPVOID lpParameter)
{
// ---------------------------------------------------------
// Create HTTP object
// ---------------------------------------------------------
Http http;
// ---------------------------------------------------------
// Set connection's parameters
// ---------------------------------------------------------
try {
http.set_hostname(hostname);
http.set_url(url);
}
catch ( Http_memory_error e )
{
ExitThread (THREAD_MEM_ERROR);
return 1;
}
http.set_port(port);
http.set_timeout(timeout);
// ---------------------------------------------------------
// Get the web document
// ---------------------------------------------------------
try { http.get(); }
catch ( Http_memory_error e )
{
ExitThread (THREAD_MEM_ERROR);
return 1;
}
catch ( Http_invalid_args e )
{
ExitThread (THREAD_INVALID_ARGS);
return 1;
}
catch ( Http_connect_failed e )
{
switch (e.get_error())
{
case SCK_CREATE_ERROR:
ExitThread (THREAD_CREATE_SOCK_ERR);
return 1;
case SCK_CONNECT_ERROR:
ExitThread (THREAD_CONNECT_FAILED);
return 1;
case SCK_GET_IP_ERROR:
ExitThread (THREAD_RESOLV_FAILED);
return 1;
case SCK_SOCK_INIT_DLL_ERR:
ExitThread (THREAD_DLL_ERR);
return 1;
case SCK_SOCK_INIT_DLL_CHECK_ERR:
ExitThread (THREAD_DLL_VERSION);
return 1;
case SCK_SET_NON_BLOCK_ERR:
ExitThread (THREAD_NON_BLOCK_ERR);
return 1;
case SCK_SET_BLOCK_ERR:
ExitThread (THREAD_BLOCK_ERROR);
return 1;
default:
ExitThread (THREAD_UNKNOWN_ERR);
return 1;
}
return 1;
}
catch ( Http_write_failed e )
{
ExitThread (THREAD_WRITE_ERR);
return 1;
}
catch ( Http_read_failed e )
{
ExitThread (THREAD_READ_ERR);
return 1;
}
catch ( Http_timeout_connect e )
{
ExitThread (THREAD_CONNECT_TIMEOUT);
return 1;
}
catch ( Http_timeout_read e )
{
ExitThread (THREAD_READ_TIMEOUT);
return 1;
}
catch ( Http_timer_error e )
{
ExitThread (THREAD_TIMER_ERR);
return 1;
}
ExitThread (THREAD_OK);
return 0;
}
// -------------------------------------------------------------
// Main entry point
// -------------------------------------------------------------
int main (int argc, char *argv[])
{
// ---------------------------------------------------------
// Check connection's parameters
// ---------------------------------------------------------
if (argc != 6)
{
print_usage();
return 1;
}
// ---------------------------------------------------------
// Initialize global variables
// ---------------------------------------------------------
port = atoi(argv[3]);
timeout = atoi(argv[4]);
hostname = new char[strlen(argv[1])+1];
if (hostname == NULL)
{
cerr << endl
<< "Can not allocate memory for hostname!"
<< endl
<< endl;
return 1;
}
url = new char[strlen(argv[2])+1];
if (url == NULL)
{
cerr << endl
<< "Can not allocate memory for url!"
<< endl
<< endl;
delete [] hostname;
return 1;
}
strcpy (hostname, argv[1]);
strcpy (url, argv[2]);
int nthreads = atoi(argv[5]);
// ---------------------------------------------------------
// Print parameter summury
// ---------------------------------------------------------
cout << endl
<< "Configuration:" << endl << endl
<< " Port: " << port << endl
<< " Timeout: " << timeout << endl
<< " hostname: " << hostname << endl
<< " url: " << url << endl
<< " number fo threads: " << nthreads << " (maximum is: "
<< MAXIMUM_WAIT_OBJECTS << ")" << endl;
// ---------------------------------------------------------
// Allocate control structures
// ---------------------------------------------------------
// Handles for all threads
HANDLE *h_threads = new HANDLE[nthreads];
if (h_threads == NULL)
{
cerr << endl
<< "Can not allocate memory for threads's handles!"
<< endl
<< endl;
delete [] hostname;
delete [] url;
return 1;
}
// Ids for all threads
DWORD *id_threads = new DWORD[nthreads];
if (id_threads == NULL)
{
cerr << endl
<< "Can not allocate memory for threads's IDs!"
<< endl
<< endl;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 1;
}
int number_of_threads = 0;
// Handles for threads that have been successfuly launched only
HANDLE *open_thread = new HANDLE[nthreads];
if (open_thread == NULL)
{
cerr << endl
<< "Can not allocate memory for good threads!"
<< endl
<< endl;
delete [] id_threads;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 1;
}
// Return values for threads that have been successfuly launched only
DWORD *return_values = new DWORD[nthreads];
if (return_values == NULL)
{
cerr << endl
<< "Can not allocate memory for threads's return values!"
<< endl
<< endl;
delete [] open_thread;
delete [] id_threads;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 1;
}
// ---------------------------------------------------------
// Launch threads
// ---------------------------------------------------------
cout << endl
<< "Launching threads:"
<< endl
<< flush;
for (int i=0; i<nthreads; i++)
{
cout << endl
<< " - thread number "
<< i
<< flush;
h_threads[i] = CreateThread (
NULL,
0,
Thread_HTTP_Proc,
NULL,
SYNCHRONIZE,
&(id_threads[i])
);
if (h_threads[i] == NULL)
{
DWORD sys_error = GetLastError();
LPVOID error_msg;
DWORD cr = FormatMessage (
FORMAT_MESSAGE_FROM_SYSTEM ¦
FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
sys_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &error_msg,
0,
NULL
);
cerr << endl
<< "ERROR: Can not create new thread number "
<< i
<< endl
<< (LPTSTR)error_msg
<< endl
<< endl;
LocalFree(error_msg);
}
else
{
// One more thread sucessfuly launched
open_thread[number_of_threads++] = h_threads[i];
}
}
// ---------------------------------------------------------
// Wait for all threads to terminate
// ---------------------------------------------------------
cout << endl
<< endl
<< "Wait for all threads to terminate ... (global timeout is " << timeout*2 << " seconds)"
<< flush;
DWORD status = WaitForMultipleObjects (
number_of_threads, // Number of handles to wait for
open_thread, // Handles' list
TRUE, // Wait for all handles
(timeout*2)*1000 // wait forever.
);
if (status == WAIT_FAILED)
{
cout << endl << "ERROR !!!!" << endl << flush;
DWORD sys_error = GetLastError();
LPVOID error_msg;
DWORD cr = FormatMessage (
FORMAT_MESSAGE_FROM_SYSTEM ¦
FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
sys_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &error_msg,
0,
NULL
);
cerr << endl
<< "ERROR: Error while waiting for threads to terminate."
<< endl
<< (LPTSTR)error_msg
<< endl
<< endl;
LocalFree(error_msg);
for (int k=0; k<number_of_threads; k++)
{
TerminateThread (open_thread[k], 0);
CloseHandle (open_thread[k]);
}
delete [] return_values;
delete [] open_thread;
delete [] id_threads;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 1;
}
// ---------------------------------------------------------
// Get threads return values
// ---------------------------------------------------------
int r_error = 0;
int r_timeout_connect = 0;
int r_timeout_read = 0;
int r_ok = 0;
for (i=0; i<number_of_threads; i++)
{
DWORD status;
// -----------------------------------------------------
// Get thread's exit code
// -----------------------------------------------------
bool cr = GetExitCodeThread (
open_thread[i], // handle to the thread
&status // address to receive termination status
);
if (cr == 0)
{
DWORD sys_error = GetLastError();
LPVOID error_msg;
DWORD cr = FormatMessage (
FORMAT_MESSAGE_FROM_SYSTEM ¦
FORMAT_MESSAGE_ALLOCATE_BUFFER ¦
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
sys_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &error_msg,
0,
NULL
);
cerr << endl
<< "ERROR: Error while getting thread's exit code"
<< endl
<< (LPTSTR)error_msg
<< endl
<< endl;
LocalFree(error_msg);
for (int k=0; k<number_of_threads; k++)
{
TerminateThread (open_thread[k], 0);
CloseHandle (open_thread[k]);
}
delete [] return_values;
delete [] open_thread;
delete [] id_threads;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 1;
}
// -----------------------------------------------------
// Analyse the thread's exit code
// -----------------------------------------------------
switch (status)
{
case THREAD_OK:
r_ok++;
break;
case THREAD_CONNECT_TIMEOUT:
r_timeout_connect++;
break;
case THREAD_READ_TIMEOUT:
r_timeout_read++;
break;
default:
r_error++;
}
return_values[i] = status;
}
// ---------------------------------------------------------
// Print statistics
// ---------------------------------------------------------
cout << endl << endl;
cout << "Results:" << endl;
cout << endl << " - Number of successful connections: " << r_ok;
cout << endl << " - Number of errors: " << r_error;
cout << endl << " - Number of connect timeout: " << r_timeout_connect;
cout << endl << " - Number of read timeout: " << r_timeout_read;
cout << endl << endl;
// ---------------------------------------------------------
// Close all handles
// ---------------------------------------------------------
cout << endl
<< "Close all threads' handles"
<< flush;
for (int k=0; k<number_of_threads; k++)
{
TerminateThread (open_thread[k], 0);
CloseHandle (open_thread[k]);
}
// ---------------------------------------------------------
// Delete all allocated variable
// ---------------------------------------------------------
cout << endl
<< "Delete all allocated memory"
<< flush;
delete [] return_values;
delete [] open_thread;
delete [] id_threads;
delete [] h_threads;
delete [] hostname;
delete [] url;
return 0;
}
|
|