/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.tools.throughputtest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.text.DecimalFormat;
/**
* This program defines a simple client that will establish a
* connection to the throughput test server and read data from it as quickly as
* the server sends it. The client can run for a given maximum duration or
* until a specified number of bytes have been transferred.
*
*
* @author Neil A. Wilson
*/
public class ThroughputTestClient
{
// Indicates whether the client should stop reading data from the server.
protected boolean shouldStop;
// The buffer used to hold data read from the server.
private ByteBuffer readBuffer;
// The maximum number length of time in seconds that the client should run.
private int maxDuration;
// The port to use to connect to the server.
private int serverPort;
// The maximum number of bytes of data to read from the server.
private long maxBytes;
// The number of bytes read so far from the server.
private long bytesRead;
// The time that the client started reading data from the server.
private long startTime;
// The time that the client stopped reading data from the server.
private long stopTime;
// The address to use to connect to the server.
private String serverAddress;
/**
* Parses the command-line arguments and creates a new throughput test client
* based on the provided information.
*
* @param args The command-line arguments to use for this program.
*/
public static void main(String[] args)
{
// Set default values for the command-line arguments.
int bufferSize = ThroughputTestServer.DEFAULT_BUFFER_SIZE;
int maxDuration = -1;
int serverPort = ThroughputTestServer.DEFAULT_LISTEN_PORT;
long maxBytes = -1;
String serverAddress = "127.0.0.1";
// Parse the arguments provided to the program.
for (int i=0; i < args.length; i++)
{
if (args[i].equals("-h"))
{
serverAddress = args[++i];
}
else if (args[i].equals("-p"))
{
serverPort = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-B"))
{
bufferSize = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-b"))
{
maxBytes = Long.parseLong(args[++i]);
}
else if (args[i].equals("-d"))
{
maxDuration = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-H"))
{
displayUsage();
System.exit(0);
}
else
{
System.err.println("Unrecognized argument \"" + args[i] + '"');
displayUsage();
System.exit(1);
}
}
// Make sure that either a max duration or max bytes was specified.
if ((maxBytes <= 0) && (maxDuration <= 0))
{
System.err.println("Either maximum number of bytes (-b) or maximum " +
"duration (-d) must be greater than zero");
displayUsage();
System.exit(1);
}
// Create the client and do the work.
ThroughputTestClient client =
new ThroughputTestClient(serverAddress, serverPort, bufferSize,
maxBytes, maxDuration);
try
{
client.readData();
}
catch (IOException ioe)
{
System.err.println("Unable to connect to throughput test server: " +
ioe);
ioe.printStackTrace();
System.exit(1);
}
long bytesRead = client.getBytesRead();
long elapsedTime = client.getDurationMillis();
double bytesPerSecond = 1.0 * bytesRead / (elapsedTime / 1000);
double bitsPerSecond = bytesPerSecond * 8;
double kilobytesPerSecond = bytesPerSecond / 1024;
double kilobitsPerSecond = bitsPerSecond/ 1024;
double megabytesPerSecond = kilobytesPerSecond / 1024;
double megabitsPerSecond = kilobitsPerSecond / 1024;
DecimalFormat decimalFormat = new DecimalFormat("0.000");
System.out.println("Read " + bytesRead + " bytes in " + elapsedTime +
" milliseconds");
System.out.println("Bits per second: " +
decimalFormat.format(bitsPerSecond));
System.out.println("Bytes per Second: " +
decimalFormat.format(bytesPerSecond));
System.out.println("Kilobits per Second: " +
decimalFormat.format(kilobitsPerSecond));
System.out.println("Kilobytes per Second: " +
decimalFormat.format(kilobytesPerSecond));
System.out.println("Megabits per Second: " +
decimalFormat.format(megabitsPerSecond));
System.out.println("Megabytes per Second: " +
decimalFormat.format(megabytesPerSecond));
}
/**
* Creates a new throughput test client with the provided information. Note
* that at least one of maxBytes and maxDuration must be greater than zero.
*
* @param serverAddress The address of the throughput test server.
* @param serverPort The port of the throughput test server.
* @param bufferSize The buffer size to use when reading data.
* @param maxBytes The maximum number of bytes to read from the server.
* @param maxDuration The maximum length of time in seconds to read data.
*/
public ThroughputTestClient(String serverAddress, int serverPort,
int bufferSize, long maxBytes, int maxDuration)
{
this.serverAddress = serverAddress;
this.serverPort = serverPort;
this.maxBytes = maxBytes;
this.maxDuration = maxDuration;
startTime = 0;
stopTime = 0;
bytesRead = 0;
readBuffer = ByteBuffer.allocateDirect(bufferSize);
}
/**
* Establishes a connection to the throughput test server then reads data in a
* loop until either the maximum number of bytes have been read, the client
* has run for the maximum duration, or an error occurs while reading data
* from the server.
*
* @throws IOException If a problem occurs while establishing the connection
* to the server.
*/
public void readData()
throws IOException
{
InetSocketAddress socketAddress =
new InetSocketAddress(serverAddress, serverPort);
SocketChannel clientChannel = SocketChannel.open(socketAddress);
clientChannel.configureBlocking(true);
if (maxDuration > 0)
{
new ThroughputTestClientTimer(this, maxDuration).start();
}
startTime = System.currentTimeMillis();
while (! shouldStop)
{
try
{
int bufferBytesRead = clientChannel.read(readBuffer);
readBuffer.clear();
if (bufferBytesRead > 0)
{
bytesRead += bufferBytesRead;
if ((maxBytes > 0) && (bytesRead >= maxBytes))
{
shouldStop = true;
}
}
else
{
System.err.println("Unexpected end of input stream from server");
shouldStop = true;
}
}
catch (Exception e)
{
System.err.println("Error reading data from server: " + e);
shouldStop = true;
}
}
stopTime = System.currentTimeMillis();
try
{
clientChannel.close();
} catch (Exception e) {}
}
/**
* Retrieves the length of time in milliseconds that the client was reading
* data from the server.
*
* @return The length of time in milliseconds that the client was reading
* data from the server.
*/
public long getDurationMillis()
{
return (stopTime - startTime);
}
/**
* Retrieves the total number of bytes read from the server.
*
* @return The total number of bytes read from the server.
*/
public long getBytesRead()
{
return bytesRead;
}
/**
* Displays usage information for this program.
*/
public static void displayUsage()
{
String EOL = System.getProperty("line.separator");
System.err.println(
"Usage: java ThroughputTestClient [options]" + EOL +
" where [options] include:" + EOL +
"-h [addr] -- Specifies the address of the throughput server" + EOL +
"-p [port] -- Specifies the port of the throughput server" + EOL +
"-b [size] -- Specifies the maximum number of bytes to read from the server" +
EOL +
"-d [time] -- Specifies the maximum length of time in seconds to read" + EOL +
" data from the server" + EOL +
"-B [size] -- Specifies the size in bytes of the read buffer to use" + EOL +
"-H -- Displays this usage information"
);
}
}