/*******************************************************************************
* Copyright 2013-2015 alladin-IT GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package at.alladin.rmbt.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import at.alladin.rmbt.client.helper.TestStatus;
public class RMBTTest extends AbstractRMBTTest implements Callable<ThreadTestResult>
{
private static final long nsecsL = 1000000000L;
// private static final double nsecs = 1e9;
private static final long UPLOAD_MAX_DISCARD_TIME = 1 * nsecsL;
private static final long UPLOAD_MAX_WAIT_SECS = 3;
private final CyclicBarrier barrier;
private final AtomicBoolean fallbackToOneThread;
private final boolean doDownload = true;
private final boolean doUpload = true;
private final AtomicLong curTransfer = new AtomicLong();
private final AtomicLong curTime = new AtomicLong();
private final long minDiffTime;
private final int maxCoarseResults;
private final int maxFineResults;
private class SingleResult
{
private final Results fine;
private final Results coarse;
private int fineResults = 0;
private int coarseResults = 0;
SingleResult()
{
fine = new Results(maxFineResults);
coarse = new Results(maxCoarseResults);
}
@Override
public String toString() {
return "SingleResult [fine=" + fine + ", coarse=" + coarse
+ ", fineResults=" + fineResults + ", coarseResults="
+ coarseResults + "]";
}
public void addResult(final long newBytes, final long newNsec)
{
boolean addToCoarse = coarseResults == 0;
if (! addToCoarse)
{
final long diffTime = newNsec - coarse.nsec[(coarseResults - 1) % coarse.nsec.length];
if (diffTime > minDiffTime)
addToCoarse = true;
}
if (coarse.bytes.length > 0) {
if (addToCoarse)
{
int coarsePos = coarseResults++ % coarse.bytes.length;
coarse.bytes[coarsePos] = newBytes;
coarse.nsec[coarsePos] = newNsec;
}
int finePos = fineResults++ % fine.bytes.length;
fine.bytes[finePos] = newBytes;
fine.nsec[finePos] = newNsec;
}
}
// @SuppressWarnings("unused")
// void logResult(final String type)
// {
// log(String.format(Locale.US, "thread %d - Time Diff %d", threadId, nsec));
// log(String.format(Locale.US, "thread %d: %.0f kBit/s %s (%.2f kbytes / %.3f secs)", threadId, getSpeed() / 1e3, type,
// getBytes() / 1e3, getNsec() / nsecs));
// }
// // bit/s
// double getSpeed()
// {
// return (double) getBytes() / (double) getNsec() * nsecs * 8.0;
// }
public long getBytes()
{
if (fineResults == 0)
return 0;
else
return fine.bytes[(fineResults - 1) % fine.bytes.length];
}
public long getNsec()
{
if (fineResults == 0)
return 0;
else
return fine.nsec[(fineResults - 1) % fine.nsec.length];
}
public Results getAllResults()
{
final int numResultsCoarse = Math.min(coarseResults, maxCoarseResults);
final int numResultsFine = Math.min(fineResults, maxFineResults);
final int numResults = numResultsCoarse + numResultsFine;
long[] resultBytes = new long[numResults];
long[] resultNsec = new long[numResults];
int results = 0;
int posCoarse = coarseResults - numResultsCoarse;
int posFine = fineResults - numResultsFine;
while (results < numResults && (posCoarse < coarseResults || posFine < fineResults))
{
final boolean coarseAvail = posCoarse < coarseResults;
final boolean fineAvail = posFine < fineResults;
final long thisCoarse = coarseAvail ? coarse.nsec[posCoarse % coarse.nsec.length] : -1;
final long thisFine = fineAvail ? fine.nsec[posFine % fine.nsec.length] : -1;
if ((thisFine <= thisCoarse || thisCoarse == -1) && fineAvail)
{
resultNsec[results] = thisFine;
resultBytes[results++] = fine.bytes[posFine++ % fine.bytes.length];
if (thisFine == thisCoarse && coarseAvail)
posCoarse++;
}
else if ((thisCoarse < thisFine || thisFine == -1) && coarseAvail)
{
resultNsec[results] = thisCoarse;
resultBytes[results++] = coarse.bytes[posCoarse++ % coarse.bytes.length];
}
else // shoudn't happen; avoid endless loop
break;
}
if (results < numResults)
{
// resultBytes = Arrays.copyOf(resultBytes, results); // copyOf not avail in android sdk < 9
// resultNsec = Arrays.copyOf(resultNsec, results);
long[] newResultBytes = new long[results];
long[] newResultNsec = new long[results];
System.arraycopy(resultBytes, 0, newResultBytes, 0, results);
System.arraycopy(resultNsec, 0, newResultNsec, 0, results);
resultBytes = newResultBytes;
resultNsec = newResultNsec;
}
final Results result = new Results(resultBytes, resultNsec);
return result;
}
public void addCoarseSpeedItems(List<SpeedItem> list, boolean upload, int thread)
{
long lastNsec = 0;
final int numResultsCoarse = Math.min(coarseResults, maxCoarseResults);
for (int i = 0; i < numResultsCoarse; i++)
{
final long nsec = coarse.nsec[i % coarse.nsec.length];
final long bytes = coarse.bytes[i % coarse.bytes.length];
final SpeedItem item = new SpeedItem(upload, thread, nsec, bytes);
list.add(item);
lastNsec = nsec;
}
final long nsec = getNsec();
if (nsec > lastNsec)
{
final long bytes = getBytes();
final SpeedItem item = new SpeedItem(upload, thread, nsec, bytes);
list.add(item);
}
}
}
public RMBTTest(final RMBTClient client, final RMBTTestParameter params, final int threadId,
final CyclicBarrier barrier, final int storeResults, final long minDiffTime,
final AtomicBoolean fallbackToOneThread)
{
super (client, params, threadId);
this.barrier = barrier;
this.maxCoarseResults = storeResults;
this.maxFineResults = storeResults;
this.minDiffTime = minDiffTime;
this.fallbackToOneThread = fallbackToOneThread;
}
static class CurrentSpeed
{
long trans;
long time;
@Override
public String toString() {
return "CurrentSpeed [trans=" + trans + ", time=" + time + "]";
}
}
public CurrentSpeed getCurrentSpeed(CurrentSpeed result)
{
if (result == null)
result = new CurrentSpeed();
result.trans = curTransfer.get();
result.time = curTime.get();
return result;
}
protected Socket connect(final TestResult testResult) throws IOException
{
log(String.format(Locale.US, "thread %d: connecting...", threadId));
final InetAddress inetAddress = InetAddress.getByName(params.getHost());
System.out.println("connecting to: " + inetAddress.getHostName() + ":" + params.getPort());
final Socket s = getSocket(inetAddress.getHostAddress(), params.getPort(), true, 20000);
testResult.ip_local = s.getLocalAddress();
testResult.ip_server = s.getInetAddress();
testResult.port_remote = s.getPort();
if (s instanceof SSLSocket)
{
final SSLSocket sslSocket = (SSLSocket) s;
final SSLSession session = sslSocket.getSession();
testResult.encryption = String.format(Locale.US, "%s (%s)", session.getProtocol(), session.getCipherSuite());
}
log(String.format(Locale.US, "thread %d: ReceiveBufferSize: '%s'.", threadId, s.getReceiveBufferSize()));
log(String.format(Locale.US, "thread %d: SendBufferSize: '%s'.", threadId, s.getSendBufferSize()));
if (in != null)
totalDown += in.getCount();
if (out != null)
totalUp += out.getCount();
in = new InputStreamCounter(s.getInputStream());
reader = new BufferedReader(new InputStreamReader(in, "US-ASCII"), 4096);
out = new OutputStreamCounter(s.getOutputStream());
String line = reader.readLine();
if (!line.equals(EXPECT_GREETING))
{
log(String.format(Locale.US, "thread %d: got '%s' expected '%s'", threadId, line, EXPECT_GREETING));
return null;
}
line = reader.readLine();
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
return null;
}
final String send = String.format(Locale.US, "TOKEN %s\n", params.getToken());
out.write(send.getBytes("US-ASCII"));
line = reader.readLine();
if (line == null)
{
log(String.format(Locale.US, "thread %d: got no answer expected 'OK'", threadId, line));
return null;
}
else if (!line.equals("OK"))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'OK'", threadId, line));
return null;
}
line = reader.readLine();
final Scanner scanner = new Scanner(line);
try
{
if (!"CHUNKSIZE".equals(scanner.next()))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'CHUNKSIZE'", threadId, line));
return null;
}
try
{
chunksize = scanner.nextInt();
log(String.format(Locale.US, "thread %d: CHUNKSIZE is %d", threadId, chunksize));
}
catch (final Exception e)
{
log(String.format(Locale.US, "thread %d: invalid CHUNKSIZE: '%s'", threadId, line));
return null;
}
if (buf == null || buf != null && buf.length != chunksize)
buf = new byte[chunksize];
return s;
}
finally
{
scanner.close();
}
}
public ThreadTestResult call()
{
log(String.format(Locale.US, "thread %d: started.", threadId));
final ThreadTestResult testResult = new ThreadTestResult();
Socket s = null;
try
{
s = connect(testResult);
if (s == null)
throw new Exception("error during connect to test server");
log(String.format(Locale.US, "thread %d: connected, waiting for rest...", threadId));
barrier.await();
/***** short download *****/
{
final long targetTimeEnd = System.nanoTime() + params.getPretestDuration() * nsecsL;
int chunks = 1;
do
{
downloadChunks(chunks);
chunks *= 2;
}
while (System.nanoTime() < targetTimeEnd);
if (chunks <= 4)
// connection is quite slow, we'll only use 1 thread
fallbackToOneThread.set(true);
}
/*********************/
boolean _fallbackToOneThread;
setStatus(TestStatus.PING);
/***** ping *****/
{
barrier.await();
startTrafficService(TestStatus.PING);
_fallbackToOneThread = fallbackToOneThread.get();
if (_fallbackToOneThread && threadId != 0)
return null;
final int NUMPINGS = params.getNumPings();
long shortestPing = Long.MAX_VALUE;
long medianPing = Long.MAX_VALUE;
long[] pings = new long[NUMPINGS];
final long timeStart = System.nanoTime();
if (threadId == 0) // only one thread pings!
{
for (int i = 0; i < NUMPINGS; i++)
{
final Ping ping = ping();
if (ping != null)
{
client.updatePingStatus(timeStart, i+1, System.nanoTime());
pings[i] = ping.server;
if (ping.client < shortestPing)
shortestPing = ping.client;
testResult.pings.add(ping);
}
}
// median
Arrays.sort(pings);
int middle = ((pings.length) / 2);
if(pings.length % 2 == 0){
long medianA = pings[middle];
long medianB = pings[middle-1];
medianPing = (medianA + medianB) / 2;
} else{
medianPing = pings[middle + 1];
}
// display median ping
client.setPing(medianPing);
}
testResult.ping_shortest = shortestPing;
testResult.ping_median = medianPing;
}
/*********************/
if (doDownload)
{
final int duration = params.getDuration();
//final int duration = 1;
setStatus(TestStatus.DOWN);
/***** download *****/
if (!_fallbackToOneThread)
barrier.await();
stopTrafficService(TestStatus.PING);
startTrafficService(TestStatus.DOWN);
curTransfer.set(0);
curTime.set(0);
final SingleResult result = new SingleResult();
final boolean reinitSocket = download(duration, 0, result);
if (reinitSocket)
{
s.close();
s = connect(testResult);
log(String.format(Locale.US, "thread %d: reconnected", threadId));
if (s == null)
throw new Exception("error during connect to test server");
}
testResult.down = result.getAllResults();
result.addCoarseSpeedItems(testResult.speedItems, false, threadId);
// if (threadId == 0) {
// System.out.println("download speed items: " + testResult.speedItems);
// System.out.println("download raw results: " + result);
// }
curTransfer.set(result.getBytes());
curTime.set(result.getNsec());
/*********************/
}
if (doUpload)
{
final int duration = params.getDuration();
//final int duration = 1;
setStatus(TestStatus.INIT_UP);
/***** short upload *****/
{
if (!_fallbackToOneThread)
barrier.await();
stopTrafficService(TestStatus.DOWN);
curTransfer.set(0);
curTime.set(0);
final long targetTimeEnd = System.nanoTime() + params.getPretestDuration() * nsecsL;
int chunks = 1;
do
{
uploadChunks(chunks);
chunks *= 2;
}
while (System.nanoTime() < targetTimeEnd);
}
/*********************/
/***** upload *****/
setStatus(TestStatus.UP);
startTrafficService(TestStatus.UP);
curTransfer.set(0);
curTime.set(0);
if (!_fallbackToOneThread)
barrier.await();
final SingleResult result = new SingleResult();
upload(duration, result);
testResult.up = result.getAllResults();
result.addCoarseSpeedItems(testResult.speedItems, true, threadId);
if (in != null)
totalDown += in.getCount();
if (out != null)
totalUp += out.getCount();
testResult.totalDownBytes = totalDown;
testResult.totalUpBytes = totalUp;
curTransfer.set(result.getBytes());
curTime.set(result.getNsec());
stopTrafficService(TestStatus.UP);
/*********************/
}
}
catch (final BrokenBarrierException e)
{
client.log("interrupted (BBE)");
Thread.currentThread().interrupt();
}
catch (final InterruptedException e)
{
client.log("interrupted");
Thread.currentThread().interrupt();
}
catch (final Exception e)
{
client.log(e);
client.abortTest(true);
}
finally
{
if (s != null)
try
{
s.close();
}
catch (final IOException e)
{
client.log(e);
}
}
return testResult;
}
private void downloadChunks(final int chunks) throws InterruptedException, IOException
{
if (Thread.interrupted())
throw new InterruptedException();
if (chunks < 1)
throw new IllegalArgumentException();
log(String.format(Locale.US, "thread %d: getting %d chunk(s)", threadId, chunks));
String line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
throw new IllegalStateException();
}
String send;
send = String.format(Locale.US, "GETCHUNKS %d\n", chunks);
out.write(send.getBytes("US-ASCII"));
out.flush();
// long expectBytes = chunksize * chunks;
long totalRead = 0;
long read;
byte lastByte = (byte) 0;
do
{
if (Thread.interrupted())
throw new InterruptedException();
read = in.read(buf);
if (read > 0)
{
final int posLast = chunksize - 1 - (int) (totalRead % chunksize);
if (read > posLast)
lastByte = buf[posLast];
totalRead += read;
}
}
while (read > 0 && lastByte != (byte) 0xff);
send = "OK\n";
out.write(send.getBytes("US-ASCII"));
out.flush();
line = reader.readLine(); // read TIME line
}
/**
* perform single donwload test
*
* @param seconds
* requested duration of the test
* @param result
* SingleResult object to store the results in
* @return true if the socket needs to be reinitialized, false if can be
* reused
* @throws IOException
* @throws UnsupportedEncodingException
* @throws InterruptedException
* @throws IllegalStateException
*/
private boolean download(final int seconds, final int additionalWait, final SingleResult result)
throws IOException, UnsupportedEncodingException, InterruptedException, IllegalStateException
{
if (Thread.interrupted())
throw new InterruptedException();
if (seconds < 1)
throw new IllegalArgumentException();
log(String.format(Locale.US, "thread %d: download test %d seconds", threadId, seconds));
String line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
throw new IllegalStateException();
}
final long timeStart = System.nanoTime();
final long timeLatestEnd = timeStart + (seconds + additionalWait) * nsecsL;
String send;
send = String.format(Locale.US, "GETTIME %d\n", seconds);
out.write(send.getBytes("US-ASCII"));
out.flush();
long totalRead = 0;
long read;
byte lastByte = (byte) 0;
do
{
if (Thread.interrupted())
throw new InterruptedException();
read = in.read(buf);
if (read > 0)
{
final int posLast = chunksize - 1 - (int) (totalRead % chunksize);
if (read > posLast)
lastByte = buf[posLast];
totalRead += read;
final long nsec = System.nanoTime() - timeStart;
result.addResult(totalRead, nsec);
curTransfer.set(totalRead);
curTime.set(nsec);
}
}
while (read > 0 && lastByte != (byte) 0xff && System.nanoTime() <= timeLatestEnd);
final long timeEnd = System.nanoTime();
if (read <= 0)
{
log(String.format(Locale.US, "thread %d: error while receiving data", threadId));
throw new IllegalStateException();
}
final long nsec = timeEnd - timeStart;
result.addResult(totalRead, nsec);
curTransfer.set(totalRead);
curTime.set(nsec);
if (lastByte != (byte) 0xff)
return true;
send = "OK\n";
out.write(send.getBytes("US-ASCII"));
out.flush();
line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
final Scanner s = new Scanner(line);
s.findInLine("TIME (\\d+)");
s.close();
// result.nsecServer = Long.parseLong(s.match().group(1));
return false;
}
private void uploadChunks(final int chunks) throws InterruptedException, IOException
{
if (Thread.interrupted())
throw new InterruptedException();
if (chunks < 1)
throw new IllegalArgumentException();
log(String.format(Locale.US, "thread %d: putting %d chunk(s)", threadId, chunks));
String line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
throw new IllegalStateException();
}
out.write("PUTNORESULT\n".getBytes("US-ASCII"));
out.flush();
line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.equals("OK"))
throw new IllegalStateException();
buf[chunksize - 1] = (byte) 0; // set last byte to continue value
for (int i = 0; i < chunks; i++)
{
if (i == chunks - 1)
buf[chunksize - 1] = (byte) 0xff; // set last byte to
// termination value
out.write(buf, 0, chunksize);
}
line = reader.readLine(); // TIME line
}
/**
* @param seconds
* requested duration of the test
* @param result
* SingleResult object to store the results in
* @return true if the socket needs to be reinitialized, false if can be
* reused
* @throws IOException
* @throws UnsupportedEncodingException
* @throws InterruptedException
* @throws IllegalStateException
*/
private boolean upload(final int seconds, final SingleResult result) throws IOException,
UnsupportedEncodingException, InterruptedException, IllegalStateException
{
if (Thread.interrupted())
throw new InterruptedException();
if (seconds < 1 && !params.isEncryption())
throw new IllegalArgumentException();
log(String.format(Locale.US, "thread %d: upload test %d seconds", threadId, seconds));
long _enoughTime = (seconds - UPLOAD_MAX_DISCARD_TIME) * nsecsL;
if (_enoughTime < 0)
_enoughTime = 0;
final long enoughTime = _enoughTime;
String line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
throw new IllegalStateException();
}
out.write("PUT\n".getBytes("US-ASCII"));
out.flush();
line = reader.readLine();
if (line == null)
throw new IllegalStateException("connection lost");
if (!line.equals("OK"))
throw new IllegalStateException();
final AtomicBoolean terminateRxIfEnough = new AtomicBoolean(false);
final AtomicBoolean terminateRxAtAllEvents = new AtomicBoolean(false);
final Future<Boolean> futureRx = RMBTClient.getCommonThreadPool().submit(new Callable<Boolean>()
{
public Boolean call() throws Exception
{
final Pattern patternFull = Pattern.compile("TIME (\\d+) BYTES (\\d+)");
final Pattern patternTime = Pattern.compile("TIME (\\d+)");
final Scanner s = new Scanner(reader);
try
{
s.useDelimiter("\n");
boolean terminate = false;
do
{
String next = null;
try
{
next = s.next(patternFull);
}
catch (final InputMismatchException e)
{
}
if (next == null)
{
next = s.next(patternTime);
if (next == null)
{
System.out.println(s.nextLine());
throw new IllegalStateException();
}
return false;
}
final MatchResult match = s.match();
if (match.groupCount() == 2)
{
final long nsec = Long.parseLong(match.group(1));
final long bytes = Long.parseLong(match.group(2));
result.addResult(bytes, nsec);
curTransfer.set(bytes);
curTime.set(nsec);
}
if (terminateRxAtAllEvents.get())
terminate = true;
if (terminateRxIfEnough.get() && curTime.get() > enoughTime)
terminate = true;
}
while (! terminate);
return true;
}
finally
{
s.close();
}
}
});
final long maxnsecs = seconds * 1000000000L;
buf[chunksize - 1] = (byte) 0x00; // set last byte to continue value
final byte[] bufTx = buf.clone();
final AtomicBoolean terminateTx = new AtomicBoolean(false);
final Future<Void> futureTx = RMBTClient.getCommonThreadPool().submit(new Callable<Void>()
{
public Void call() throws Exception
{
for (;;)
{
if (Thread.interrupted())
throw new InterruptedException();
if (terminateTx.get())
{
// last package
bufTx[chunksize - 1] = (byte) 0xff; // set last byte to termination value
out.write(bufTx, 0, chunksize);
// forces buffered bytes to be written out.
out.flush();
return null;
}
else
out.write(bufTx, 0, chunksize);
}
}
});
Boolean returnValue = null;
try
{
try
{
futureTx.get(maxnsecs, TimeUnit.NANOSECONDS);
// System.out.println("futureTx regular");
}
catch (final TimeoutException e)
{
try
{
terminateTx.set(true);
futureTx.get(250, TimeUnit.MILLISECONDS);
// System.out.println("futureTx after 250");
}
catch (final TimeoutException e2)
{
futureTx.cancel(true);
// System.out.println("futureTx cancel");
}
}
Thread.sleep(100);
terminateRxIfEnough.set(true);
try
{
returnValue = futureRx.get(UPLOAD_MAX_WAIT_SECS, TimeUnit.SECONDS);
// System.out.println("futureRx regular");
}
catch (final TimeoutException e)
{
try
{
terminateRxAtAllEvents.set(true);
returnValue = futureRx.get(250, TimeUnit.MILLISECONDS);
// System.out.println("futureRx after 250");
}
catch (final TimeoutException e2)
{
futureRx.cancel(true);
// System.out.println("futureRx cancel");
}
}
}
catch (final ExecutionException e)
{
if (e.getCause() instanceof IOException)
throw (IOException) e.getCause();
else
e.printStackTrace();
}
if (returnValue == null)
returnValue = true;
return returnValue;
}
private Ping ping() throws IOException
{
log(String.format(Locale.US, "thread %d: ping test", threadId));
final long pingTimeNs = System.nanoTime();
String line = reader.readLine();
if (!line.startsWith("ACCEPT "))
{
log(String.format(Locale.US, "thread %d: got '%s' expected 'ACCEPT'", threadId, line));
return null;
}
final byte[] data = "PING\n".getBytes("US-ASCII");
final long timeStart = System.nanoTime();
out.write(data);
out.flush();
line = reader.readLine();
final long timeEnd = System.nanoTime();
out.write("OK\n".getBytes("US-ASCII"));
out.flush();
if (!line.equals("PONG"))
return null;
line = reader.readLine();
final Scanner s = new Scanner(line);
s.findInLine("TIME (\\d+)");
s.close();
final long diffClient = timeEnd - timeStart;
final long diffServer = Long.parseLong(s.match().group(1));
final double pingClient = diffClient / 1e6;
final double pingServer = diffServer / 1e6;
log(String.format(Locale.US, "thread %d - client: %.3f ms ping", threadId, pingClient));
log(String.format(Locale.US, "thread %d - server: %.3f ms ping", threadId, pingServer));
return new Ping(diffClient, diffServer, pingTimeNs);
}
private void setStatus(final TestStatus status)
{
if (threadId == 0)
client.setStatus(status);
}
private void startTrafficService(final TestStatus status) {
client.startTrafficService(threadId, status);
}
private void stopTrafficService(final TestStatus status) {
client.stopTrafficMeasurement(threadId, status);
}
}