Skip to content

Commit

Permalink
Merge pull request 'New option to specify how long test should run.' …
Browse files Browse the repository at this point in the history
…(#2) from time into main

Reviewed-on: https://git.data.coop/nellemann/jnetperf/pulls/2
  • Loading branch information
mnellemann committed Jul 14, 2023
2 parents 77b7984 + 8d6c7f8 commit df69b2e
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 75 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ You need Java (JRE) version 8 or later to run jnetperf.
- Run **/opt/jnetperf/bin/jperf**, if installed from package, or as **java -jar /path/to/jnetperf.jar**

```shell
Usage: jnetperf [-huV] [-l=SIZE] [-n=NUM] [-p=PORT] (-c=SERVER | -s)
Usage: jnetperf [-huV] [-l=NUM] [-n=NUM] [-p=NUM] [-t=SEC] (-c=SRV | -s)
For more information visit https://git.data.coop/nellemann/jnetperf
-c, --connect=SERVER Connect to remote server.
-h, --help Show this help message and exit.
-l, --pkt-len=SIZE Packet size in bytes [default: 1432].
-n, --pkt-num=NUM Number of packets to send [default: 150000].
-p, --port=PORT Network port [default: 4445].
-s, --server Run server and wait for client.
-u, --udp Use UDP network protocol [default: false].
-V, --version Print version information and exit.
-c, --connect=SRV Connect to remote server (client).
-h, --help Show this help message and exit.
-l, --pkt-len=NUM Packet size in bytes (client) [default: 1432].
-n, --pkt-num=NUM Number of packets to send (client) [default: 150000].
-p, --port=NUM Network port [default: 4445].
-s, --server Run server and wait for client (server).
-t, --runtime=SEC Time to run, supersedes pkt-num (client) [default: 0].
-u, --udp Use UDP network protocol [default: false].
-V, --version Print version information and exit.
```


Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
projectId = jnetperf
projectGroup = biz.nellemann.jnetperf
projectVersion = 0.0.7
projectVersion = 0.0.8
21 changes: 14 additions & 7 deletions src/main/java/biz/nellemann/jnetperf/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import picocli.CommandLine.Command;

import java.io.IOException;
import java.util.Locale;
import java.util.concurrent.Callable;


Expand All @@ -32,20 +33,23 @@ public class Application implements Callable<Integer> {
RunMode runMode;

static class RunMode {
@CommandLine.Option(names = { "-c", "--connect" }, required = true, description = "Connect to remote server.", paramLabel = "HOST")
@CommandLine.Option(names = { "-c", "--connect" }, required = true, description = "Connect to remote server (client).", paramLabel = "SRV")
String remoteServer;

@CommandLine.Option(names = { "-s", "--server" }, required = true, description = "Run server and wait for client.")
@CommandLine.Option(names = { "-s", "--server" }, required = true, description = "Run server and wait for client (server).")
boolean runServer = false;
}

@CommandLine.Option(names = { "-l", "--pkt-len" }, paramLabel = "NUM", description = "Packet size in bytes [default: ${DEFAULT-VALUE}].", converter = SuffixConverter.class)
@CommandLine.Option(names = { "-l", "--pkt-len" }, paramLabel = "NUM", description = "Packet size in bytes (client) [default: ${DEFAULT-VALUE}].", converter = UnitSuffixConverter.class)
int packetSize = Payload.DEFAULT_LENGTH;

@CommandLine.Option(names = { "-n", "--pkt-num" }, paramLabel = "NUM", description = "Number of packets to send [default: ${DEFAULT-VALUE}].", converter = SuffixConverter.class)
@CommandLine.Option(names = { "-n", "--pkt-num" }, paramLabel = "NUM", description = "Number of packets to send (client) [default: ${DEFAULT-VALUE}].", converter = UnitSuffixConverter.class)
int packetCount = 150_000;

@CommandLine.Option(names = { "-p", "--port" }, paramLabel = "PORT", description = "Network port [default: ${DEFAULT-VALUE}].")
@CommandLine.Option(names = { "-t", "--runtime" }, paramLabel = "SEC", description = "Time to run, precedes pkt-num (client) [default: ${DEFAULT-VALUE}].", converter = TimeSuffixConverter.class)
int timeInSeconds = 0;

@CommandLine.Option(names = { "-p", "--port" }, paramLabel = "NUM", description = "Network port [default: ${DEFAULT-VALUE}].")
int port = 4445;

@CommandLine.Option(names = { "-u", "--udp" }, description = "Use UDP network protocol [default: ${DEFAULT-VALUE}].")
Expand All @@ -56,6 +60,9 @@ static class RunMode {
@Override
public Integer call() {

// Set locale to en_US to ensure correct/identical number formatting
Locale.setDefault(new Locale("en", "US"));

try {
if (runMode.runServer) {
runServer();
Expand Down Expand Up @@ -87,11 +94,11 @@ private void runClient(String remoteHost) throws InterruptedException, IOExcepti
if(packetSize > Payload.MAX_UDP_LENGTH) {
packetSize = Payload.MAX_UDP_LENGTH;
}
UdpClient udpClient = new UdpClient(remoteHost, port, packetSize, packetCount, 0);
UdpClient udpClient = new UdpClient(remoteHost, port, packetSize, packetCount, timeInSeconds);
udpClient.start();

} else {
TcpClient tcpClient = new TcpClient(remoteHost, port, packetSize, packetCount, 0);
TcpClient tcpClient = new TcpClient(remoteHost, port, packetSize, packetCount, timeInSeconds);
tcpClient.start();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/biz/nellemann/jnetperf/Payload.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
public class Payload {

public final static int MIN_LENGTH = 64;
public final static int MAX_UDP_LENGTH = 65507;
public final static int MAX_UDP_LENGTH = 64000;
public final static int DEFAULT_LENGTH = 1432;
public final static int HEADER_LENGTH = 32;

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/biz/nellemann/jnetperf/Statistics.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class Statistics {
private long packetsPerSec;
private long packetsUnacked = 0;
private int tickItererations = 0;
private int tickTotal = 0;

private final long[] bytesPerSecAvgTmp = new long[MAX_TICKS_AVG];
private final long[] packetsPerSecAvgTmp = new long[MAX_TICKS_AVG];
Expand Down Expand Up @@ -67,6 +68,7 @@ public void tick() {
if(tickItererations % LOG_AVG_MODULO == 0) {
printAverage();
}
tickTotal++;

}

Expand Down Expand Up @@ -121,6 +123,11 @@ public long getPacketsTransferredTotal() {
}


public int getRuntime() {
return tickTotal;
}


private long getAverage(long[] array, long fallback) {
long avg = getAverage(array);
if(avg < 1) {
Expand Down
55 changes: 35 additions & 20 deletions src/main/java/biz/nellemann/jnetperf/TcpClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@

import java.io.*;
import java.net.*;
import java.util.concurrent.atomic.AtomicBoolean;

public class TcpClient {

final Logger log = LoggerFactory.getLogger(TcpClient.class);

private Statistics statistics;
private final Statistics statistics;

private DataOutputStream out;
private DataInputStream in;
Expand All @@ -21,18 +22,18 @@ public class TcpClient {
private Socket socket;

private final byte[] inBuffer = new byte[Payload.DEFAULT_LENGTH];
private final int packetCount;
private final int packetSize;
private final int packetTime;
private final int packets;
private final int length;
private final int runtime;


public TcpClient(String hostname, int port, int size, int maxPackets, int maxTime) throws IOException {
public TcpClient(String hostname, int port, int length, int packets, int runtime) throws IOException {
log.info("TcpClient() - target: {}, port: {}", hostname, port);

this.port = port;
this.packetSize = size;
this.packetCount = maxPackets;
this.packetTime = maxTime;
this.length = length;
this.packets = packets;
this.runtime = runtime;

address = InetAddress.getByName(hostname);
statistics = new Statistics();
Expand Down Expand Up @@ -61,39 +62,53 @@ private void close() throws IOException {

public void start() throws IOException, InterruptedException {

AtomicBoolean keepRunning = new AtomicBoolean(true);
Thread shutdownHook = new Thread(() -> {
keepRunning.set(false);
System.out.println("Stopping jnetperf, please wait ...");
});
Runtime.getRuntime().addShutdownHook(shutdownHook);

long sequence = 0;
socket = new Socket(address, port);
in = new DataInputStream(socket.getInputStream());
out = new DataOutputStream(socket.getOutputStream());

long sequence = 0;

// Send handshake
Payload payload = new Payload(PayloadType.HANDSHAKE.getValue(), packetSize, sequence++, packetCount);
Payload payload = new Payload(PayloadType.HANDSHAKE.getValue(), length, sequence++, packets);
send(payload);

payload = receive();
if(payload.getType() != PayloadType.ACK.getValue()) {
log.warn("No ACK!");
return;
}

// Data datagrams ...
for(int i = 0; i < packetCount; i++) {
payload = new Payload(PayloadType.DATA.getValue(), packetSize, sequence++, packetCount);
// Send data
do {
payload = new Payload(PayloadType.DATA.getValue(), length, sequence++, packets);
send(payload);
payload = receive();
if(payload.getType() != PayloadType.ACK.getValue()) {
log.warn("No ACK!");
}
statistics.tick();
}

// End datagram
//Thread.sleep(100);
payload = new Payload(PayloadType.END.getValue(), packetSize, sequence++, packetCount);
send(payload);
if (sequence > packets) {
System.out.println("Max packets reached");
keepRunning.set(false);;
}

if(runtime > 0 && statistics.getRuntime() > runtime) {
System.out.println("Max runtime reached");
keepRunning.set(false);
}

// TODO: Wait for ACK
} while (keepRunning.get());

// Send end
payload = new Payload(PayloadType.END.getValue(), length, sequence++, packets);
send(payload);
payload = receive();
statistics.ack();
if(payload.getType() != PayloadType.ACK.getValue()) {
Expand Down
17 changes: 7 additions & 10 deletions src/main/java/biz/nellemann/jnetperf/TcpServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class TcpServer extends Thread {

final Logger log = LoggerFactory.getLogger(TcpServer.class);

private final int port;
private ServerSocket socket;
private DataInputStream in;
private DataOutputStream out;
Expand All @@ -20,23 +21,20 @@ public class TcpServer extends Thread {

public TcpServer(int port) throws IOException {
log.info("TcpServer()");

socket = new ServerSocket(port);
socket.setSoTimeout(0); // Wait indefinitely

this.port = port;
}


public void run() {

boolean running = true;

try {
while (running) {
while (true) {
socket = new ServerSocket(port);
socket.setSoTimeout(0); // Wait indefinitely
inBuffer = new byte[Payload.DEFAULT_LENGTH];
session();
socket.close();
}
socket.close();
} catch(IOException e) {
log.error(e.getMessage());
}
Expand Down Expand Up @@ -96,8 +94,7 @@ public void session() throws IOException {

private Payload receive() throws IOException {
in.readFully(inBuffer);
Payload payload = new Payload(inBuffer);
return payload;
return new Payload(inBuffer);
}

}
42 changes: 42 additions & 0 deletions src/main/java/biz/nellemann/jnetperf/TimeSuffixConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package biz.nellemann.jnetperf;

import picocli.CommandLine;

import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TimeSuffixConverter implements CommandLine.ITypeConverter<Integer> {

final private Pattern pattern = Pattern.compile("(\\d+)([smh])?", Pattern.CASE_INSENSITIVE);

public Integer convert(String value) {
int seconds = 0;

Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
int number = Integer.parseInt(matcher.group(1));
if(matcher.group(2) != null) { // We got the second, minute or hour suffix
String suffix = matcher.group(2);
switch (suffix.toLowerCase(Locale.ROOT)) {
case "s":
seconds = number;
break;
case "m":
seconds = number * 60;
break;
case "h":
seconds = number * 60 * 60;
break;
default:
System.err.println("Unknown suffix: " + suffix);
seconds = number;
}
} else {
seconds = number;
}
}
return seconds;
}

}
Loading

0 comments on commit df69b2e

Please sign in to comment.