401 lines
9.8 KiB
C++
401 lines
9.8 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2014, University in Hagen,
|
|
Faculty of Mathematics and Computer Science,
|
|
Database Systems for New Applications.
|
|
|
|
SECONDO is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SECONDO is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with SECONDO; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
----
|
|
|
|
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
|
|
//paragraph [10] Footnote: [{\footnote{] [}}]
|
|
//[ue] [\"u]
|
|
//[ae] [\"a]
|
|
//[_] [\_]
|
|
//[TOC] [\tableofcontents]
|
|
|
|
|
|
|
|
[1] Loadgenerator. This program generates CSV data and send the
|
|
data through a network socket. A sample line looks like:
|
|
|
|
gEpYm0eUDk,fAgVgUHWPo,bClSVK17HX,ixjXTTW7yh,qdsU8WzP1O,
|
|
CcZw52F47W,bpRKKsoq0m,YoNOJWsGtt,c5U92XBHbG,kA5CUO4GE2
|
|
|
|
You can specify the number of lines to be send and parameter
|
|
like delay, number of columns or the size of a column.
|
|
*/
|
|
|
|
/*
|
|
1.0 Includes
|
|
|
|
*/
|
|
#include <iostream>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <netdb.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <netinet/in.h>
|
|
#include <boost/concept_check.hpp>
|
|
|
|
#include "timer.h"
|
|
|
|
/*
|
|
1.1 Defines
|
|
|
|
*/
|
|
|
|
#define EOT "\004"
|
|
#define ACK "\006"
|
|
|
|
#define CMDLINE_HOST 1<<0
|
|
#define CMDLINE_PORT 1<<1
|
|
#define CMDLINE_LINES 1<<2
|
|
#define CMDLINE_DELAY 1<<3
|
|
#define CMDLINE_COLUMNS 1<<4
|
|
#define CMDLINE_SIZE 1<<5
|
|
#define CMDLINE_ACK 1<<6
|
|
|
|
using namespace std;
|
|
|
|
/*
|
|
1.2 Structs
|
|
|
|
*/
|
|
|
|
struct commandline_args_t {
|
|
char *hostname; // Hostname
|
|
int port; // Port to connect to
|
|
int lines; // Lines to send
|
|
int delay; // Delay in ms
|
|
int columns; // Number of columns
|
|
int sizePerColumn; // Size per column
|
|
int acknowledgeAfter;// Wait for ack after n lines
|
|
};
|
|
|
|
// Allowed chars to send
|
|
static const char charArray[] =
|
|
"0123456789"
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijlkmnopqrstuvwxyz"
|
|
"01"; // Padding to 64 Byte
|
|
|
|
|
|
static unsigned int g_seed;
|
|
|
|
//Used to seed the generator.
|
|
//Code from: https://software.intel.com/en-us/articles/
|
|
// fast-random-number-generator-on-the-intel-pentiumr-4-processor/
|
|
|
|
inline void fast_srand( int seed ) {
|
|
g_seed = seed;
|
|
}
|
|
|
|
//fastrand routine returns one integer, similar output value range as C lib.
|
|
inline int fastrand() {
|
|
g_seed = (214013*g_seed+2531011);
|
|
return (g_seed>>16)&0x7FFF;
|
|
}
|
|
|
|
/*
|
|
2.1 Print usage fuction
|
|
|
|
*/
|
|
void printUsageAndExit(char* progname) {
|
|
cerr << "Usage: " << progname << " -h <hostname> -p <port> -l <lines> "
|
|
"-d <delay> -c <columns> -s <size per column> -a <ack after>"
|
|
<< endl;
|
|
cerr << endl;
|
|
cerr << "Where <hostname> is the hostname to connect to" << endl;
|
|
cerr << "<port> is the port to connect to" << endl;
|
|
cerr << "<lines> is the numer of lines to send" << endl;
|
|
cerr << "<delay> is the pause (in ms) between two lines " << endl;
|
|
cerr << "<columns> is the number of columns to generate" << endl;
|
|
cerr << "<size per column> is the size in byte per column" << endl;
|
|
cerr << "<ack after> wait fror a ACK from server after n lines" << endl;
|
|
cerr << endl;
|
|
cerr << "Example: " << progname
|
|
<< " -h 127.0.0.1 -p 10000 -l 10 -d 4 -c 10 -s 10 -a 10" << endl;
|
|
exit(-1);
|
|
}
|
|
|
|
/*
|
|
2.2 Fill the send buffer with the specified content
|
|
|
|
*/
|
|
void fillBuffer(char *buffer, int columns, int sizePerColumn) {
|
|
size_t pos = 0;
|
|
|
|
for(size_t i = 0; i < columns; i++) {
|
|
|
|
if(i != 0) {
|
|
*(buffer + pos) = ',';
|
|
++pos;
|
|
}
|
|
|
|
int arrayPos = fastrand() & 0x3F;
|
|
|
|
for(size_t charNum = 0; charNum < sizePerColumn; ++charNum) {
|
|
// Access to the charArray 64 Byte
|
|
// 0x3F = 00111111 (Coveres the last 64 Byte)
|
|
*(buffer + pos) = charArray[arrayPos];
|
|
++pos;
|
|
}
|
|
}
|
|
|
|
*(buffer + pos) = '\n';
|
|
*(buffer + pos + 1) = '\0';
|
|
}
|
|
|
|
/*
|
|
2.3 Wait for an ACK char from remote server
|
|
|
|
*/
|
|
void waitForAck(int socketfd) {
|
|
|
|
char buffer[255];
|
|
size_t bytes = read(socketfd, buffer, sizeof(buffer));
|
|
|
|
/*if(strncmp(buffer,ACK,1) == 0) {
|
|
cout << "[Info] Got ack";
|
|
} else {
|
|
cout << "[Error] Got something else";
|
|
}*/
|
|
}
|
|
|
|
/*
|
|
2.4 Open the network socket
|
|
|
|
*/
|
|
bool openSocket(int &socketfd, char* hostname, int port) {
|
|
|
|
struct hostent *server;
|
|
struct sockaddr_in server_addr;
|
|
|
|
socketfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if(socketfd < 0) {
|
|
cerr << "Error opening socket" << endl;
|
|
return false;
|
|
}
|
|
|
|
// Resolve hostname
|
|
server = gethostbyname(hostname);
|
|
|
|
if(server == NULL) {
|
|
cerr << "Error resolving hostname: " << hostname << endl;
|
|
return -1;
|
|
}
|
|
|
|
// Connect
|
|
memset(&server_addr, 0, sizeof(server_addr));
|
|
server_addr.sin_family = AF_INET;
|
|
server_addr.sin_port = htons(port);
|
|
|
|
server_addr.sin_addr.s_addr =
|
|
((struct in_addr *)server->h_addr_list[0])->s_addr;
|
|
|
|
if(connect(socketfd, (struct sockaddr*) &server_addr,
|
|
sizeof(struct sockaddr)) < 0) {
|
|
|
|
cerr << "Error in connect() " << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
2.5 Parse commandline args amd fill struct commandline\_args
|
|
|
|
*/
|
|
void parseCommandline(int argc, char* argv[],
|
|
commandline_args_t &commandline_args) {
|
|
|
|
unsigned int flags = 0;
|
|
|
|
// Default values
|
|
commandline_args.acknowledgeAfter = -1;
|
|
commandline_args.delay = 0;
|
|
|
|
int option = 0;
|
|
while ((option = getopt(argc, argv,"h:p:l:d:c:s:a:")) != -1) {
|
|
switch (option) {
|
|
case 'h':
|
|
commandline_args.hostname = optarg;
|
|
flags |= CMDLINE_HOST;
|
|
break;
|
|
case 'p':
|
|
commandline_args.port = atoi(optarg);
|
|
flags |= CMDLINE_PORT;
|
|
break;
|
|
case 'l':
|
|
commandline_args.lines = atoi(optarg);
|
|
flags |= CMDLINE_LINES;
|
|
break;
|
|
case 'd':
|
|
commandline_args.delay = atoi(optarg);
|
|
flags |= CMDLINE_DELAY;
|
|
break;
|
|
case 'c':
|
|
commandline_args.columns = atoi(optarg);
|
|
flags |= CMDLINE_COLUMNS;
|
|
break;
|
|
case 's':
|
|
commandline_args.sizePerColumn = atoi(optarg);
|
|
flags |= CMDLINE_SIZE;
|
|
break;
|
|
case 'a':
|
|
commandline_args.acknowledgeAfter = atoi(optarg);
|
|
flags |= CMDLINE_ACK;
|
|
break;
|
|
default:
|
|
printUsageAndExit(argv[0]);
|
|
}
|
|
}
|
|
|
|
unsigned int requriedFlags = CMDLINE_HOST | CMDLINE_PORT | CMDLINE_LINES
|
|
| CMDLINE_COLUMNS | CMDLINE_SIZE ;
|
|
|
|
if((flags & requriedFlags) != requriedFlags) {
|
|
printUsageAndExit(argv[0]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
2.6 Write data to the socket
|
|
|
|
*/
|
|
bool writeData(const int socketfd, const char *data, const size_t len) {
|
|
int ret = 0;
|
|
|
|
for (int n = 0; n < len; ) {
|
|
ret = write(socketfd, (char *)data + n, len - n);
|
|
if (ret < 0) {
|
|
if (errno == EINTR || errno == EAGAIN) {
|
|
continue;
|
|
}
|
|
break;
|
|
} else {
|
|
n += ret;
|
|
}
|
|
}
|
|
|
|
// All data was written successfully
|
|
if(ret > 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
2.7 Main function
|
|
|
|
The function open a socket, generate requested data and close the socket
|
|
|
|
*/
|
|
int main(int argc, char* argv[]) {
|
|
|
|
// Our output socket
|
|
int socketfd;
|
|
|
|
// Commandline args
|
|
commandline_args_t commandline_args;
|
|
parseCommandline(argc, argv, commandline_args);
|
|
|
|
// Initalize Rand
|
|
fast_srand (time(NULL));
|
|
|
|
// Open socket
|
|
if(! openSocket(socketfd, commandline_args.hostname,
|
|
commandline_args.port) ) {
|
|
|
|
cerr << "[Error] Unable to open socket" << endl;
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Prepare buffer
|
|
size_t bufferSize = commandline_args.columns
|
|
* commandline_args.sizePerColumn + commandline_args.columns + 1;
|
|
|
|
char *buffer = (char*) malloc(bufferSize * sizeof(char));
|
|
|
|
fillBuffer(buffer, commandline_args.columns,
|
|
commandline_args.sizePerColumn);
|
|
|
|
cout << "The size of the buffer is: " << bufferSize
|
|
<< " bytes " << endl;
|
|
cout << "The buffer contains: " << buffer << endl;
|
|
|
|
// Start timer
|
|
Timer timer;
|
|
timer.start();
|
|
|
|
// Calculate progess (i)
|
|
cout << "Writing: ";
|
|
int fivePercents =
|
|
max(((int) ((commandline_args.lines / 100.0) * 5.0)), 1);
|
|
|
|
// Write lines to server
|
|
for(int i = 0; i < commandline_args.lines; ++i) {
|
|
|
|
fillBuffer(buffer, commandline_args.columns,
|
|
commandline_args.sizePerColumn);
|
|
|
|
// -1 because we dont want so send the \0 terminal
|
|
writeData(socketfd, buffer, bufferSize - 1);
|
|
|
|
// Calculate progess (ii)
|
|
if(i % fivePercents == 0) {
|
|
cout << ".";
|
|
cout << flush;
|
|
}
|
|
|
|
// Wait for ack
|
|
if(commandline_args.acknowledgeAfter > 0 &&
|
|
((i + 1) % commandline_args.acknowledgeAfter == 0)) {
|
|
waitForAck(socketfd);
|
|
}
|
|
|
|
if(commandline_args.delay != 0) {
|
|
usleep(commandline_args.delay * 1000);
|
|
}
|
|
}
|
|
|
|
cout << endl;
|
|
cout << "Total execution time (ms): " << timer.getDiff() / 1000 << endl;
|
|
|
|
// Send EOT (End of Transmission)
|
|
writeData(socketfd, EOT, sizeof(char));
|
|
shutdown(socketfd, 2);
|
|
|
|
if(buffer != NULL) {
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|