//Copyright 2012 RobustNet Lab, University of Michigan. All Rights Reserved.
package com.mobilyzer.measurements;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.Random;
import com.mobilyzer.MeasurementDesc;
import com.mobilyzer.MeasurementResult;
import com.mobilyzer.MeasurementTask;
import com.mobilyzer.MeasurementResult.TaskProgress;
import com.mobilyzer.exceptions.MeasurementError;
import com.mobilyzer.util.Logger;
import com.mobilyzer.util.MLabNS;
import com.mobilyzer.util.MeasurementJsonConvertor;
import com.mobilyzer.util.PhoneUtils;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author Haokun Luo
*
* TCP Throughput is a measurement task for cellular network throughput.
* 1. Uplink: the mobile device continuously sends packets with consistent
* packet size. We send packets for a fixed amount of time, and we sample
* each throughput value at a smaller period. We use the median of all the
* sampling result as the final measurement result. The result is calculated
* at the server side, and send back to the device.
* 2. Downlink: similar methodology as uplink. Only difference is that the
* device is receiving packets from the server, and calculate the result
* locally.
*/
public class TCPThroughputTask extends MeasurementTask {
// default constant here
public static final String DESCRIPTOR = "TCP Speed Test";
public static final int PORT_DOWNLINK = 6001;
public static final int PORT_UPLINK = 6002;
public static final int PORT_CONFIG = 6003;
public static final String TYPE = "tcpthroughput";
// Timing related
public final int BUFFER_SIZE = 5000;
public static final long DURATION_IN_SEC = 15;
public final int KSEC = 1000;
public static final long SAMPLE_PERIOD_IN_SEC = 1;
public static final long SLOW_START_PERIOD_IN_SEC = 5;
public static final int TCP_TIMEOUT_IN_SEC = 30;
// largest non-fragment packet size in LTE (uplink)
public static final int THROUGHPUT_UP_PKT_SIZE_MAX = 1357;
public static final int THROUGHPUT_UP_PKT_SIZE_MIN = 700;
// Data related
private final int KBYTE = 1024;
private static final int DATA_LIMIT_MB_UP = 5;
private static final int DATA_LIMIT_MB_DOWN = 10;
private boolean DATA_LIMIT_ON = true;
private boolean DATA_LIMIT_EXCEEDED = false;
private static final String UPLINK_FINISH_MSG = "*";
private Context context = null;
// helper variables
private int accumulativeSize = 0;
private Random randStr = new Random();
private ArrayList<Double> samplingResults = new ArrayList<Double>();
//start time of each sampling period
private long startSampleTime = 0;
private String serverVersion = "";
private long taskStartTime = 0;
private double taskDuration = 0;
//uplink accumulative data
private int totalSendSize = 0;
// downlink accumulative data
private int totalRevSize = 0;
private long duration;
private TaskProgress taskProgress;
private volatile boolean stopFlag;
// class constructor
public TCPThroughputTask(MeasurementDesc desc) {
super(new TCPThroughputDesc(desc.key, desc.startTime, desc.endTime,
desc.intervalSec, desc.count, desc.priority, desc.contextIntervalSec,
desc.parameters));
this.taskProgress=TaskProgress.FAILED;
this.stopFlag=false;
this.duration=(long)(this.KSEC*
((TCPThroughputDesc)measurementDesc).duration_period_sec +
((TCPThroughputDesc)measurementDesc).slow_start_period_sec);
Logger.i("Create new throughput task");
}
protected TCPThroughputTask(Parcel in) {
super(in);
taskProgress = (TaskProgress)in.readSerializable();
stopFlag = in.readByte() != 0;
duration = in.readLong();
}
public static final Parcelable.Creator<TCPThroughputTask> CREATOR =
new Parcelable.Creator<TCPThroughputTask>() {
public TCPThroughputTask createFromParcel(Parcel in) {
return new TCPThroughputTask(in);
}
public TCPThroughputTask[] newArray(int size) {
return new TCPThroughputTask[size];
}
};
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeSerializable(taskProgress);
dest.writeByte((byte) (stopFlag ? 1 : 0));
dest.writeLong(duration);
}
/**
* There are seven parameters specifically for this experiment:
* 1. data_limit_mb_up: uplink cellular network data limit
* 2. data_limit_mb_down: downlink cellular network data limit
* 3. duration_period_sec : downlink maximum experiment duration period
* 4. pkt_size_up_bytes: the size each packet in the uplink
* 5. sample_period_sec : the small interval to calculate current throughput result
* 6. slow_start_period_sec : waiting period to avoid TCP slow start
* 7. tcp_timeout_sec: TCP connection timeout
*/
public static class TCPThroughputDesc extends MeasurementDesc {
// declared parameters
public double data_limit_mb_up =
TCPThroughputTask.DATA_LIMIT_MB_UP;
public double data_limit_mb_down =
TCPThroughputTask.DATA_LIMIT_MB_DOWN;
public boolean dir_up = false;
public double duration_period_sec =
TCPThroughputTask.DURATION_IN_SEC;
public int pkt_size_up_bytes =
TCPThroughputTask.THROUGHPUT_UP_PKT_SIZE_MAX;
public double sample_period_sec =
TCPThroughputTask.SAMPLE_PERIOD_IN_SEC;
public double slow_start_period_sec =
TCPThroughputTask.SLOW_START_PERIOD_IN_SEC;
public String target = null;
public double tcp_timeout_sec = TCPThroughputTask.TCP_TIMEOUT_IN_SEC;
public TCPThroughputDesc(String key, Date startTime,
Date endTime, double intervalSec, long count,
long priority, int contextIntervalSec, Map<String, String> params)
throws InvalidParameterException {
super(TCPThroughputTask.TYPE, key, startTime, endTime, intervalSec, count,
priority, contextIntervalSec, params);
initializeParams(params);
if (this.target == null || this.target.length() == 0) {
throw new InvalidParameterException("TCPThroughputTask null target");
}
}
protected TCPThroughputDesc(Parcel in) {
super(in);
data_limit_mb_up = in.readDouble();
data_limit_mb_down = in.readDouble();
dir_up = in.readByte() != 0;
duration_period_sec = in.readDouble();
pkt_size_up_bytes = in.readInt();
sample_period_sec = in.readDouble();
slow_start_period_sec = in.readDouble();
target = in.readString();
tcp_timeout_sec = in.readDouble();
}
public static final Parcelable.Creator<TCPThroughputDesc> CREATOR
= new Parcelable.Creator<TCPThroughputDesc>() {
public TCPThroughputDesc createFromParcel(Parcel in) {
return new TCPThroughputDesc(in);
}
public TCPThroughputDesc[] newArray(int size) {
return new TCPThroughputDesc[size];
}
};
@Override
public int describeContents() {
return super.describeContents();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeDouble(data_limit_mb_up);
dest.writeDouble(data_limit_mb_down);
dest.writeByte((byte) (dir_up ? 1 : 0));
dest.writeDouble(duration_period_sec);
dest.writeInt(pkt_size_up_bytes);
dest.writeDouble(sample_period_sec);
dest.writeDouble(slow_start_period_sec);
dest.writeString(target);
dest.writeDouble(tcp_timeout_sec);
}
@Override
protected void initializeParams(Map<String, String> params) {
if (params == null) {
return;
}
if ( (target = params.get("target")) == null ) {
target = MLabNS.TARGET;
}
try {
String readVal = null;
if ((readVal = params.get("data_limit_mb_down")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.data_limit_mb_down = Double.parseDouble(readVal);
if (this.data_limit_mb_down > TCPThroughputTask.DATA_LIMIT_MB_DOWN) {
this.data_limit_mb_down = TCPThroughputTask.DATA_LIMIT_MB_DOWN;
}
}
if ((readVal = params.get("data_limit_mb_up")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.data_limit_mb_up = Double.parseDouble(readVal);
if (this.data_limit_mb_up > TCPThroughputTask.DATA_LIMIT_MB_UP) {
this.data_limit_mb_up = TCPThroughputTask.DATA_LIMIT_MB_UP;
}
}
if ((readVal = params.get("duration_period_sec")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.duration_period_sec = Double.parseDouble(readVal);
if (this.duration_period_sec > TCPThroughputTask.DURATION_IN_SEC) {
this.duration_period_sec = TCPThroughputTask.DURATION_IN_SEC;
}
}
if ((readVal = params.get("pkt_size_up_bytes")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.pkt_size_up_bytes = Integer.parseInt(readVal);
if (this.pkt_size_up_bytes > TCPThroughputTask.THROUGHPUT_UP_PKT_SIZE_MAX) {
this.pkt_size_up_bytes = TCPThroughputTask.THROUGHPUT_UP_PKT_SIZE_MAX;
}
if (this.pkt_size_up_bytes < TCPThroughputTask.THROUGHPUT_UP_PKT_SIZE_MIN) {
this.pkt_size_up_bytes = TCPThroughputTask.THROUGHPUT_UP_PKT_SIZE_MIN;
}
}
if ((readVal = params.get("sample_period_sec")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.sample_period_sec = Double.parseDouble(readVal);
if (this.sample_period_sec > TCPThroughputTask.DURATION_IN_SEC/2) {
this.sample_period_sec = TCPThroughputTask.DURATION_IN_SEC/2;
}
}
if ((readVal = params.get("slow_start_period_sec")) != null
&& readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.slow_start_period_sec = Double.parseDouble(readVal);
if (this.slow_start_period_sec > TCPThroughputTask.DURATION_IN_SEC/2) {
this.slow_start_period_sec = TCPThroughputTask.DURATION_IN_SEC/2;
}
}
if ((readVal = params.get("tcp_timeout_sec")) != null &&
readVal.length() > 0 && Integer.parseInt(readVal) > 0) {
this.tcp_timeout_sec = Integer.parseInt(readVal)*1000;
if (this.tcp_timeout_sec > TCPThroughputTask.TCP_TIMEOUT_IN_SEC) {
this.tcp_timeout_sec = TCPThroughputTask.TCP_TIMEOUT_IN_SEC;
}
}
} catch (NumberFormatException e) {
throw new InvalidParameterException("TCP Throughput Task invalid parameters.");
}
String dir = null;
if ((dir = params.get("dir_up")) != null && dir.length() > 0) {
if (dir.compareTo("Up") == 0 || dir.compareTo("true") == 0) {
this.dir_up = true;
}
}
}
@Override
public String getType() {
return TCPThroughputTask.TYPE;
}
/**
* Find the median value from a TCPThroughput JSON result string (already sorted)
* Suppose N is the number of results. If N is odd, we pick the result with index
* (N-1)/2. If N is even, we take the mean value between index N/2 and N/2-1
*
* @return -1 fail to create result
* @return median value result
*/
public double calMedianSpeedFromTCPThroughputOutput(String outputInJSON) {
if (outputInJSON == null ||
outputInJSON.equals("") ||
outputInJSON.equals("[]") ||
outputInJSON.charAt(0) != '[' ||
outputInJSON.charAt(outputInJSON.length()-1) != ']') {
return -1;
}
String[] splitResult = outputInJSON.substring(1,
outputInJSON.length()-1).split(",");
int resultLen = splitResult.length;
if (resultLen <= 0)
return 0.0;
double result = 0.0;
if (resultLen % 2 == 0) {
result = (Double.parseDouble(splitResult[resultLen / 2]) +
Double.parseDouble(splitResult[resultLen / 2 - 1])) / 2;
} else {
result = Double.parseDouble(splitResult[(resultLen - 1) / 2]);
}
return result;
}
}
/**
* Make a deep cloning of the task
*/
@Override
public MeasurementTask clone() {
MeasurementDesc desc = this.measurementDesc;
TCPThroughputDesc newDesc = new TCPThroughputDesc(
desc.key, desc.startTime,
desc.endTime, desc.intervalSec, desc.count, desc.priority,
desc.contextIntervalSec, desc.parameters);
return new TCPThroughputTask(newDesc);
}
@Override
public String getType() {
return TCPThroughputTask.TYPE;
}
@Override
public String getDescriptor() {
return TCPThroughputTask.DESCRIPTOR;
}
/**
* This will be printed to the device log console. Make sure it's well
* structured and human readable
*/
@Override
public String toString() {
TCPThroughputDesc desc = (TCPThroughputDesc) measurementDesc;
String resp;
if (desc.dir_up) {
resp = "[TCP Uplink]\n";
} else {
resp = "[TCP Downlink]\n";
}
resp += " Target: " + desc.target + "\n Interval (sec): " +
desc.intervalSec + "\n Next run: " + desc.startTime;
return resp;
}
@SuppressWarnings("rawtypes")
public static Class getDescClass() throws InvalidClassException {
return TCPThroughputDesc.class;
}
@Override
public MeasurementResult[] call() throws MeasurementError {
this.taskProgress=TaskProgress.FAILED;
TCPThroughputDesc desc = (TCPThroughputDesc)measurementDesc;
// Apply MLabNS lookup to fetch FQDN
if (!desc.target.equals(MLabNS.TARGET)) {
Logger.i("Not using MLab server!");
throw new InvalidParameterException("Unknown target " + desc.target +
" for TCPThroughput");
}
try {
ArrayList<String> mlabResult = MLabNS.Lookup(context, "mobiperf");
if (mlabResult.size() == 1) {
desc.target = mlabResult.get(0);
} else {
throw new MeasurementError("Invalid MLabNS result");
}
} catch (InvalidParameterException e) {
throw new MeasurementError(e.getMessage());
}
Logger.i("Setting target to: " + desc.target);
PhoneUtils phoneUtils = PhoneUtils.getPhoneUtils();
// reset the data limit if the phone is under Wifi
if (phoneUtils.getNetwork().equals(phoneUtils.NETWORK_WIFI)) {
Logger.i("Detect Wifi network");
this.DATA_LIMIT_ON = false;
}
Logger.i("Running TCPThroughput on " + desc.target);
try {
// fetch server information
if (!acquireServerConfig()) {
throw new MeasurementError("Fail to acquire server configuration");
}
Logger.i("Server version is " + this.serverVersion);
if (desc.dir_up == true) {
uplink();
if(stopFlag){
throw new MeasurementError("Cancelled");
}
Logger.i("Uplink measurement result is:");
}
else {
this.taskStartTime = System.currentTimeMillis();
downlink();
if(stopFlag){
throw new MeasurementError("Cancelled");
}
Logger.i("Downlink measurement result is:");
}
this.taskProgress=TaskProgress.COMPLETED;
} catch (MeasurementError e) {
throw e;
} catch (IOException e) {
Logger.e("Error close the socket for " + desc.type);
throw new MeasurementError("Error close the socket for " + desc.type);
} catch (InterruptedException e) {
Logger.e("Interrupted captured");
throw new MeasurementError("Task gets interrrupted");
}
MeasurementResult result = new MeasurementResult(
phoneUtils.getDeviceInfo().deviceId,
phoneUtils.getDeviceProperty(this.getKey()), TCPThroughputTask.TYPE,
System.currentTimeMillis() * 1000, taskProgress,
this.measurementDesc);
// TODO (Haokun): add more results if necessary
result.addResult("tcp_speed_results", this.samplingResults);
result.addResult("data_limit_exceeded", this.DATA_LIMIT_EXCEEDED);
result.addResult("duration", this.taskDuration);
result.addResult("server_version", this.serverVersion);
Logger.i(MeasurementJsonConvertor.toJsonString(result));
MeasurementResult[] mrArray= new MeasurementResult[1];
mrArray[0]=result;
return mrArray;
}
/*****************************************************************
* Core measurement functions definitions
*****************************************************************
* acquire server configuration information
* 1) m-lab slice version
*
* @return: true -- successful acquire data from M-Lab slice
* @return: false -- failure to acquire data from M-Lab slice
*/
private boolean acquireServerConfig() throws MeasurementError, IOException,
InterruptedException {
Socket tcpSocket = null;
InputStream iStream = null;
boolean result = false;
try {
tcpSocket = new Socket();
buildUpSocket(tcpSocket, ((TCPThroughputDesc)measurementDesc).target,
TCPThroughputTask.PORT_CONFIG);
iStream = tcpSocket.getInputStream();
} catch (IOException e) {
throw new MeasurementError("Error open uplink socket at " +
((TCPThroughputDesc)measurementDesc).target +
" with port " +
TCPThroughputTask.PORT_CONFIG);
}
try {
// read from server side configuration
byte [] resultMsg = new byte[this.BUFFER_SIZE];
int resultMsgLen = iStream.read(resultMsg, 0, resultMsg.length);
if (resultMsgLen > 0) {
// TODO (Haokun): Maybe switch to JSON for multiple acquired data
// currently use one double number
this.serverVersion = new String(resultMsg).substring(0, resultMsgLen);
result = true;
}
} catch (IOException e) {
throw new MeasurementError("Error to acquire configuration from " +
((TCPThroughputDesc)measurementDesc).target);
} finally {
iStream.close();
tcpSocket.close();
Logger.i("Close server Config socket");
}
return result;
}
/* Uplink measurement task
* @throws IOException
* @throws InterruptedException
*/
private void uplink() throws MeasurementError, IOException, InterruptedException {
Logger.i("Start uplink task on " + ((TCPThroughputDesc)measurementDesc).target);
Socket tcpSocket = null;
InputStream iStream = null;
OutputStream oStream = null;
try {
tcpSocket = new Socket();
buildUpSocket(tcpSocket, ((TCPThroughputDesc)measurementDesc).target,
TCPThroughputTask.PORT_UPLINK);
oStream = tcpSocket.getOutputStream();
iStream = tcpSocket.getInputStream();
} catch (IOException e){
e.printStackTrace();
throw new MeasurementError("Error open uplink socket at " +
((TCPThroughputDesc)measurementDesc).target +
" with port " +
TCPThroughputTask.PORT_UPLINK);
}
long startTime = System.currentTimeMillis();
long endTime = startTime;
int data_limit_byte_up =
(int)(((TCPThroughputDesc)measurementDesc).data_limit_mb_up
*this.KBYTE*this.KBYTE);
byte[] uplinkBuffer =
new byte[((TCPThroughputDesc)measurementDesc).pkt_size_up_bytes];
this.genRandomByteArray(uplinkBuffer);
try {
long totalDuration = (long)(this.KSEC*
((TCPThroughputDesc)measurementDesc).duration_period_sec +
((TCPThroughputDesc)measurementDesc).slow_start_period_sec);
do {
if(stopFlag){
throw new MeasurementError("Cancelled");
}
oStream.write(uplinkBuffer, 0, uplinkBuffer.length);
oStream.flush();
endTime = System.currentTimeMillis();
this.totalSendSize += ((TCPThroughputDesc)measurementDesc).pkt_size_up_bytes;
if (this.DATA_LIMIT_ON &&
this.totalSendSize >= data_limit_byte_up) {
Logger.i("Detect uplink exceeding limitation " +
(double)((TCPThroughputDesc)measurementDesc).data_limit_mb_up + " MB");
this.DATA_LIMIT_EXCEEDED = true;
break;
}
// propagate every quarter
} while ((endTime - startTime) < totalDuration);
// convert into seconds
this.taskDuration = (double)(endTime - startTime) / 1000.0;
Logger.i("Uplink total data comsumption is " +
(double)this.totalSendSize/(1024*1024) + " MB");
// send last message with special content
uplinkBuffer = TCPThroughputTask.UPLINK_FINISH_MSG.getBytes();
oStream.write(uplinkBuffer, 0, uplinkBuffer.length);
oStream.flush();
// read from server side results
byte [] resultMsg = new byte[this.BUFFER_SIZE];
int resultMsgLen = iStream.read(resultMsg, 0, resultMsg.length);
if (resultMsgLen > 0) {
String resultMsgStr = new String(resultMsg).substring(0, resultMsgLen);
// Sample result string is "1111.11#2222.22#3333.33";
Logger.i("Uplink result from server is " + resultMsgStr);
String [] tps_result_str = resultMsgStr.split("#");
double sampleResult;
for (int i = 0; i < tps_result_str.length; i++) {
sampleResult = Double.valueOf(tps_result_str[i]);
this.samplingResults = this.insertWithOrder(this.samplingResults,
sampleResult);
}
}
Logger.i("Total number of sampling result is " + this.samplingResults.size());
} catch (OutOfMemoryError e) {
throw new MeasurementError("Detect out of memory during Uplink task.");
} catch (IOException e) {
throw new MeasurementError("Error to send/receive data to " +
((TCPThroughputDesc)measurementDesc).target);
} finally {
iStream.close();
oStream.close();
tcpSocket.close();
Logger.i("Close uplink socket");
}
}
/**
* Downlink measurement task
*/
private void downlink() throws MeasurementError, IOException {
Logger.i("Start downlink task on " +
((TCPThroughputDesc)measurementDesc).target);
Socket tcpSocket = null;
InputStream iStream = null;
try {
tcpSocket = new Socket();
buildUpSocket(tcpSocket, ((TCPThroughputDesc)measurementDesc).target,
TCPThroughputTask.PORT_DOWNLINK);
iStream = tcpSocket.getInputStream();
} catch (IOException i) {
Logger.e("Downlink socket opening error" + i.getCause().toString());
throw new MeasurementError("Error to open downlink socket at " +
((TCPThroughputDesc)measurementDesc).target +
" with port " +
TCPThroughputTask.PORT_DOWNLINK);
}
try {
int read_bytes = 0;
int data_limit_byte_down = (int)(this.KBYTE*this.KBYTE*
((TCPThroughputDesc)measurementDesc).data_limit_mb_down);
byte[] buffer = new byte[this.BUFFER_SIZE];
long totalDuration = (long)(this.KSEC*
((TCPThroughputDesc)measurementDesc).duration_period_sec +
((TCPThroughputDesc)measurementDesc).slow_start_period_sec);
do {
if(stopFlag){
throw new MeasurementError("Cancelled");
}
read_bytes = iStream.read(buffer, 0, buffer.length);
updateSize(read_bytes);
this.totalRevSize += read_bytes;
if (this.DATA_LIMIT_ON &&
this.totalRevSize >= data_limit_byte_down) {
Logger.i("Detect downlink data limitation exceed with " +
((TCPThroughputDesc)measurementDesc).data_limit_mb_down + " MB");
this.DATA_LIMIT_EXCEEDED = true;
break;
}
} while (read_bytes >= 0);
// convert milliseconds to seconds
this.taskDuration = (System.currentTimeMillis() -
(double) this.taskStartTime) / 1000.0;
Logger.i("Total download data is " +
(double)this.totalRevSize/(1024*1024) + " MB");
Logger.i("Total number of sampling result is " +
this.samplingResults.size());
} catch (OutOfMemoryError e) {
throw new MeasurementError("Detect out of memory at Downlink task.");
} catch (IOException e) {
throw new MeasurementError("Error to receive data from " +
((TCPThroughputDesc)measurementDesc).target);
} finally {
iStream.close();
tcpSocket.close();
Logger.i("Close downlink socket");
}
}
/*****************************************************************
* Helper functions
*****************************************************************
* update the total received packet size
* @param time period increment
*/
private void updateSize(int delta) {
double gtime = System.currentTimeMillis() - this.taskStartTime;
//ignore slow start
if (gtime<((TCPThroughputDesc)measurementDesc).slow_start_period_sec*this.KSEC)
return;
if (this.startSampleTime == 0) {
this.startSampleTime = System.currentTimeMillis();
this.accumulativeSize = 0;
}
this.accumulativeSize += delta;
double time = System.currentTimeMillis() - this.startSampleTime;
if (time < ((TCPThroughputDesc)measurementDesc).sample_period_sec*this.KSEC) {
return;
} else {
double throughput = (double)this.accumulativeSize * 8.0 / time;
this.samplingResults = this.insertWithOrder(this.samplingResults, throughput);
this.accumulativeSize = 0;
this.startSampleTime = System.currentTimeMillis();
}
}
private void buildUpSocket(Socket tcpSocket, String hostname, int portNum)
throws IOException {
TCPThroughputDesc desc = (TCPThroughputDesc) measurementDesc;
SocketAddress remoteAddr = new InetSocketAddress(hostname, portNum);
tcpSocket.connect(remoteAddr, (int)desc.tcp_timeout_sec*this.KSEC);
tcpSocket.setSoTimeout((int)desc.tcp_timeout_sec*this.KSEC);
tcpSocket.setTcpNoDelay(true);
}
private void genRandomByteArray(byte[] byteArray) {
for (int i = 0; i < byteArray.length; i++) {
byteArray[i] = (byte)('a' + randStr.nextInt(26));
}
}
// insert element with ascending order, i.e. insertion sort
private ArrayList<Double> insertWithOrder(ArrayList<Double> array, double item) {
int i;
for (i = 0; i < array.size(); i++ ) {
if (item < array.get(i)) {
break;
}
}
array.add(i,item);
return array;
}
@Override
public long getDuration() {
return this.duration;
}
@Override
public void setDuration(long newDuration) {
if(newDuration<0){
this.duration=0;
}else{
this.duration=newDuration;
}
}
@Override
public boolean stop() {
stopFlag=true;
return true;
}
/**
* Based on the measured total data sent and received, the same returned as
* a measurement result
*/
@Override
public long getDataConsumed() {
return totalSendSize + totalRevSize;
}
}