This commit is contained in:
parent
5b5cf3f372
commit
2496338b92
23
.drone.yml
Normal file
23
.drone.yml
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
kind: pipeline
|
||||
name: default
|
||||
type: docker
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: eclipse-temurin:8-jdk
|
||||
commands:
|
||||
- ./gradlew test
|
||||
- name: build
|
||||
image: eclipse-temurin:8-jdk
|
||||
environment:
|
||||
AUTH_TOKEN: # Gitea access token ENV variable
|
||||
from_secret: auth # Name of DroneCI secret exposed above
|
||||
commands:
|
||||
- ./gradlew build packages
|
||||
- for file in build/libs/*-all.jar ; do curl --user "${DRONE_REPO_OWNER}:$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done
|
||||
- for file in build/distributions/*.deb ; do curl --user "${DRONE_REPO_OWNER}:$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done
|
||||
- for file in build/distributions/*.rpm ; do curl --user "${DRONE_REPO_OWNER}:$${AUTH_TOKEN}" --upload-file "$${file}" "https://git.data.coop/api/packages/${DRONE_REPO_OWNER}/generic/${DRONE_REPO_NAME}/${DRONE_TAG}/$(basename $file)" ; done
|
||||
when:
|
||||
event:
|
||||
- tag
|
24
build.gradle
24
build.gradle
|
@ -1,18 +1,10 @@
|
|||
/*
|
||||
* This file was generated by the Gradle 'init' task.
|
||||
*
|
||||
* This generated file contains a sample Java application project to get you started.
|
||||
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
|
||||
* User Manual available at https://docs.gradle.org/8.1.1/userguide/building_java_projects.html
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'groovy'
|
||||
id 'application'
|
||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||
id "net.nemerosa.versioning" version "2.15.1"
|
||||
id "net.nemerosa.versioning" version "3.0.0"
|
||||
id "com.netflix.nebula.ospackage" version "11.2.0"
|
||||
id "com.github.johnrengelman.shadow" version "7.1.2"
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -24,19 +16,22 @@ dependencies {
|
|||
testImplementation 'org.spockframework:spock-core:2.2-groovy-3.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
|
||||
annotationProcessor 'info.picocli:picocli-codegen:4.7.4'
|
||||
implementation 'info.picocli:picocli:4.7.4'
|
||||
implementation 'org.slf4j:slf4j-api:2.0.7'
|
||||
implementation 'ch.qos.logback:logback-classic:1.3.8'
|
||||
implementation 'info.picocli:picocli:4.7.4'
|
||||
annotationProcessor 'info.picocli:picocli-codegen:4.7.4'
|
||||
}
|
||||
|
||||
// Apply a specific Java toolchain to ease working on different environments.
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(11)
|
||||
languageVersion = JavaLanguageVersion.of(8)
|
||||
}
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
|
||||
application {
|
||||
// Define the main class for the application.
|
||||
mainClass = 'biz.nellemann.jperf.App'
|
||||
|
@ -47,6 +42,9 @@ tasks.named('test') {
|
|||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
group = projectGroup
|
||||
version = projectVersion
|
||||
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
|
|
3
gradle.properties
Normal file
3
gradle.properties
Normal file
|
@ -0,0 +1,3 @@
|
|||
projectId = jperf
|
||||
projectGroup = biz.nellemann.jperf
|
||||
projectVersion = 0.0.1
|
|
@ -26,19 +26,16 @@ public class App implements Callable<Integer> {
|
|||
@CommandLine.Option(names = { "-s", "--server" }, description = "run server and wait for client")
|
||||
boolean runServer = false;
|
||||
|
||||
@CommandLine.Option(names = { "-l", "--pkt-size" }, paramLabel = "SIZE", description = "datagram size in bytes, max 65507")
|
||||
@CommandLine.Option(names = { "-l", "--pkt-size" }, paramLabel = "SIZE", description = "datagram size in bytes, max 65507 [default: ${DEFAULT-VALUE}]")
|
||||
//int packetSize = 16384; // Min: 256 Max: 65507
|
||||
int packetSize = 65507; // Min: 256 Max: 65507
|
||||
|
||||
@CommandLine.Option(names = { "-n", "--pkt-num" }, paramLabel = "NUM", description = "number of packets to send")
|
||||
@CommandLine.Option(names = { "-n", "--pkt-num" }, paramLabel = "NUM", description = "number of packets to send [default: ${DEFAULT-VALUE}]")
|
||||
int packetCount = 5000;
|
||||
|
||||
@CommandLine.Option(names = { "-p", "--port" }, paramLabel = "PORT", description = "network port")
|
||||
@CommandLine.Option(names = { "-p", "--port" }, paramLabel = "PORT", description = "network port [default: ${DEFAULT-VALUE}]")
|
||||
int port = 4445;
|
||||
|
||||
@CommandLine.Option(names = { "-w", "--send-wait" }, paramLabel = "MILLISEC", description = "delay in millis between sending packets")
|
||||
long sendWait = 3;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
@ -52,14 +49,10 @@ public class App implements Callable<Integer> {
|
|||
runClient(remoteServer);
|
||||
}
|
||||
|
||||
|
||||
//udpServer.printSummary();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// this example implements Callable, so parsing, error handling and handling user
|
||||
// requests for usage help or version help can be done with one line of code.
|
||||
public static void main(String... args) {
|
||||
int exitCode = new CommandLine(new App()).execute(args);
|
||||
System.exit(exitCode);
|
||||
|
@ -68,51 +61,12 @@ public class App implements Callable<Integer> {
|
|||
|
||||
|
||||
private void runClient(String remoteHost) throws InterruptedException, IOException {
|
||||
long sequence = 0;
|
||||
|
||||
// Start client and send some messages
|
||||
UdpClient udpClient = new UdpClient(remoteHost, port);
|
||||
|
||||
// Start datagram
|
||||
Datagram datagram = new Datagram(DataType.HANDSHAKE.getValue(), packetSize, sequence++, packetCount);
|
||||
udpClient.send(datagram);
|
||||
Thread.sleep(100);
|
||||
|
||||
// TODO: Wait for ACK
|
||||
datagram = udpClient.receive();
|
||||
if(datagram.getType() != DataType.ACK.getValue()) {
|
||||
log.warn("No ACK!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Data datagrams ...
|
||||
for(int i = 0; i < packetCount; i++) {
|
||||
datagram = new Datagram(DataType.DATA.getValue(), packetSize, sequence++, packetCount);
|
||||
udpClient.send(datagram);
|
||||
Thread.sleep(sendWait);
|
||||
}
|
||||
|
||||
// End datagram
|
||||
Thread.sleep(100);
|
||||
datagram = new Datagram(DataType.END.getValue(), packetSize, sequence++, packetCount);
|
||||
udpClient.send(datagram);
|
||||
|
||||
// TODO: Wait for ACK
|
||||
datagram = udpClient.receive();
|
||||
if(datagram.getType() != DataType.ACK.getValue()) {
|
||||
log.warn("No ACK!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
udpClient.close();
|
||||
Thread.sleep(1000);
|
||||
|
||||
udpClient.printStatistics();
|
||||
UdpClient udpClient = new UdpClient(remoteHost, port, packetCount, packetSize);
|
||||
udpClient.start();
|
||||
}
|
||||
|
||||
|
||||
private void runServer() throws SocketException, InterruptedException {
|
||||
// Start server
|
||||
UdpServer udpServer = new UdpServer(port);
|
||||
udpServer.start();
|
||||
udpServer.join();
|
||||
|
|
68
src/main/java/biz/nellemann/jperf/Statistics.java
Normal file
68
src/main/java/biz/nellemann/jperf/Statistics.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
package biz.nellemann.jperf;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
public class Statistics {
|
||||
|
||||
private long packetsTransferred, packetsTransferredTotal = 0;
|
||||
private long bytesTransferred, bytesTransferredTotal = 0;
|
||||
private long bytesPerSec, packesPerSec = 0;
|
||||
private long packetsUnacked = 0;
|
||||
|
||||
private Instant timestamp1 = Instant.now();
|
||||
private Instant timestamp2 = Instant.now();
|
||||
|
||||
|
||||
public void reset() {
|
||||
timestamp1 = Instant.now();
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
||||
timestamp2 = Instant.now();
|
||||
if(Duration.between(timestamp1, timestamp2).toMillis() >= 1000) {
|
||||
// Because we do this every second ...
|
||||
bytesPerSec = bytesTransferred;
|
||||
packesPerSec = packetsTransferred;
|
||||
timestamp1 = timestamp2;
|
||||
print();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void print() {
|
||||
System.out.printf("%-30s Status: %8d pkt/s %12d B/s %10d KB/s %8d MB/s\n", Instant.now().toString(), packesPerSec, bytesPerSec, bytesPerSec/1_000, bytesPerSec/1_000_000);
|
||||
|
||||
}
|
||||
|
||||
public void summary() {
|
||||
System.out.printf("%-29s Summary: %8d pkts %13d B %12d KB %10d MB %6d GB\n", Instant.now().toString(), packetsTransferredTotal, bytesTransferredTotal, bytesTransferredTotal /1_000, bytesTransferredTotal /1_000_000, bytesTransferredTotal/1_000_000_000);
|
||||
}
|
||||
|
||||
public void ack() {
|
||||
packetsUnacked--;
|
||||
}
|
||||
|
||||
|
||||
public void transferPacket() {
|
||||
packetsUnacked++;
|
||||
packetsTransferred++;
|
||||
packetsTransferredTotal++;
|
||||
}
|
||||
|
||||
public void transferBytes(long bytes) {
|
||||
bytesTransferred += bytes;
|
||||
bytesTransferredTotal += bytes;
|
||||
}
|
||||
|
||||
public long getPacketsUnacked() {
|
||||
return packetsUnacked;
|
||||
}
|
||||
|
||||
public long getPacketsTransferredTotal() {
|
||||
return packetsTransferredTotal;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,6 +15,8 @@ public class UdpClient {
|
|||
|
||||
final Logger log = LoggerFactory.getLogger(UdpClient.class);
|
||||
|
||||
private Statistics statistics;
|
||||
|
||||
private final int port;
|
||||
private final InetAddress address;
|
||||
private final DatagramSocket socket;
|
||||
|
@ -23,43 +25,81 @@ public class UdpClient {
|
|||
private long packetsSent = 0;
|
||||
private long bytesSent = 0;
|
||||
|
||||
private int packetCount;
|
||||
private int packetSize;
|
||||
|
||||
public UdpClient(String hostname, int port) throws UnknownHostException, SocketException {
|
||||
|
||||
public UdpClient(String hostname, int port, int packets, int size) throws UnknownHostException, SocketException {
|
||||
log.info("UdpClient() - target: {}, port: {}", hostname, port);
|
||||
this.port = port;
|
||||
socket = new DatagramSocket();
|
||||
address = InetAddress.getByName(hostname);
|
||||
this.packetCount = packets;
|
||||
this.packetSize = size;
|
||||
statistics = new Statistics();
|
||||
}
|
||||
|
||||
public void send(Datagram datagram) throws IOException {
|
||||
private void send(Datagram datagram) throws IOException {
|
||||
DatagramPacket packet = new DatagramPacket(datagram.getPayload(), datagram.getRealLength(), address, port);
|
||||
socket.send(packet);
|
||||
packetsSent++;
|
||||
bytesSent += datagram.getRealLength();
|
||||
statistics.transferPacket();
|
||||
statistics.transferBytes(datagram.getRealLength());
|
||||
}
|
||||
|
||||
public Datagram receive() throws IOException {
|
||||
private Datagram receive() throws IOException {
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
socket.receive(packet);
|
||||
return new Datagram(buf);
|
||||
}
|
||||
|
||||
public String sendEcho(String msg) throws IOException {
|
||||
log.info("send() - msg: {}", msg);
|
||||
|
||||
buf = msg.getBytes();
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, port);
|
||||
socket.send(packet);
|
||||
packet = new DatagramPacket(buf, buf.length);
|
||||
socket.receive(packet);
|
||||
return new String( packet.getData(), 0, packet.getLength() );
|
||||
}
|
||||
|
||||
public void close() {
|
||||
private void close() {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
public void printStatistics() {
|
||||
System.out.printf("%s sent: %d pkts\t %d B\t %d KB\t %d MB\n", Instant.now().toString(), packetsSent, bytesSent, bytesSent/1000, bytesSent/1_000_000);
|
||||
|
||||
public void start() throws IOException, InterruptedException {
|
||||
|
||||
long sequence = 0;
|
||||
|
||||
// Start datagram
|
||||
Datagram datagram = new Datagram(DataType.HANDSHAKE.getValue(), packetSize, sequence++, packetCount);
|
||||
send(datagram);
|
||||
|
||||
// TODO: Wait for ACK
|
||||
datagram = receive();
|
||||
if(datagram.getType() != DataType.ACK.getValue()) {
|
||||
log.warn("No ACK!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Data datagrams ...
|
||||
for(int i = 0; i < packetCount; i++) {
|
||||
datagram = new Datagram(DataType.DATA.getValue(), packetSize, sequence++, packetCount);
|
||||
send(datagram);
|
||||
datagram = receive();
|
||||
if(datagram.getType() != DataType.ACK.getValue()) {
|
||||
log.warn("No ACK!");
|
||||
}
|
||||
statistics.tick();
|
||||
}
|
||||
|
||||
// End datagram
|
||||
//Thread.sleep(100);
|
||||
datagram = new Datagram(DataType.END.getValue(), packetSize, sequence++, packetCount);
|
||||
send(datagram);
|
||||
|
||||
// TODO: Wait for ACK
|
||||
datagram = receive();
|
||||
statistics.ack();
|
||||
if(datagram.getType() != DataType.ACK.getValue()) {
|
||||
log.warn("No ACK!");
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
close();
|
||||
statistics.summary();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,8 +5,6 @@ import java.net.DatagramPacket;
|
|||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -15,13 +13,9 @@ public class UdpServer extends Thread {
|
|||
|
||||
final Logger log = LoggerFactory.getLogger(UdpServer.class);
|
||||
|
||||
|
||||
private final DatagramSocket socket;
|
||||
private byte[] buf = new byte[256];
|
||||
|
||||
long pktsReceived, pktsReceivedTotal = 0;
|
||||
long bytesReceived, bytesReceivedTotal = 0;
|
||||
long bytesPerSec, pktsPerSec = 0;
|
||||
|
||||
public UdpServer(int port) throws SocketException {
|
||||
log.info("UdpServer()");
|
||||
|
@ -46,29 +40,11 @@ public class UdpServer extends Thread {
|
|||
}
|
||||
|
||||
|
||||
public void printStatistics() {
|
||||
// Because we do this every second ...
|
||||
bytesPerSec = bytesReceived;
|
||||
pktsPerSec = pktsReceived;
|
||||
|
||||
System.out.printf("%s recv: %d pkt/s\t %d B/s\t %d KB/s\t %d MB/s\n", Instant.now().toString(), pktsPerSec, bytesPerSec, bytesPerSec/1_000, bytesPerSec/1_000_000);
|
||||
pktsReceived = 0;
|
||||
bytesReceived = 0;
|
||||
}
|
||||
|
||||
public void printSummary() {
|
||||
System.out.printf("%s recv: %d pkts\t %d B\t %d KB\t %d MB\n", Instant.now().toString(), pktsReceivedTotal, bytesReceivedTotal, bytesReceivedTotal/1_000, bytesReceivedTotal/1_000_000);
|
||||
}
|
||||
|
||||
|
||||
public void session() throws IOException {
|
||||
|
||||
Statistics statistics = new Statistics();
|
||||
boolean running = true;
|
||||
|
||||
boolean ackEnd = false;
|
||||
long thisSequence, lastSequence = 0;
|
||||
Instant startInstant = Instant.now();
|
||||
Instant checkInstant;
|
||||
|
||||
while (running) {
|
||||
|
||||
|
@ -79,61 +55,39 @@ public class UdpServer extends Thread {
|
|||
int port = packet.getPort();
|
||||
|
||||
Datagram datagram = new Datagram(buf);
|
||||
thisSequence = datagram.getCurPkt();
|
||||
|
||||
statistics.transferPacket();
|
||||
statistics.transferBytes(datagram.getRealLength());
|
||||
|
||||
if(datagram.getType() == DataType.HANDSHAKE.getValue()) {
|
||||
log.info("Handshake from ... {}, length: {}", address, datagram.getLength());
|
||||
|
||||
// Setup to receive larger datagrams
|
||||
buf = new byte[datagram.getLength()];
|
||||
|
||||
// Send ACK
|
||||
Datagram responseDatagram = new Datagram(DataType.ACK.getValue(), 32, datagram.getCurPkt(), 1);
|
||||
packet = new DatagramPacket(responseDatagram.getPayload(), responseDatagram.getLength(), address, port);
|
||||
socket.send(packet);
|
||||
|
||||
statistics.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
if(datagram.getType() == DataType.DATA.getValue()) {
|
||||
bytesReceived += datagram.getLength();
|
||||
bytesReceivedTotal += datagram.getLength();
|
||||
|
||||
if(thisSequence == lastSequence + 1) {
|
||||
//log.info("Data .... size: {}, sequence: {}", datagram.getLength(), thisSequence);
|
||||
} else {
|
||||
//log.warn("Data .... out of sequence: {} vs {}", thisSequence, lastSequence);
|
||||
}
|
||||
}
|
||||
|
||||
bytesReceived += datagram.getRealLength();
|
||||
bytesReceivedTotal += datagram.getRealLength();
|
||||
}*/
|
||||
|
||||
if(datagram.getType() == DataType.END.getValue()) {
|
||||
ackEnd = true;
|
||||
}
|
||||
|
||||
// Send ACK
|
||||
Datagram responseDatagram = new Datagram(DataType.ACK.getValue(), 32, datagram.getCurPkt(), 1);
|
||||
packet = new DatagramPacket(responseDatagram.getPayload(), responseDatagram.getLength(), address, port);
|
||||
socket.send(packet);
|
||||
statistics.ack();
|
||||
|
||||
// Every second
|
||||
checkInstant = Instant.now();
|
||||
if(Duration.between(startInstant, checkInstant).toSeconds() >= 1) {
|
||||
printStatistics();
|
||||
startInstant = checkInstant;
|
||||
}
|
||||
|
||||
if(ackEnd && pktsReceivedTotal > datagram.getMaxPkt()) {
|
||||
// Send ACK
|
||||
Datagram responseDatagram = new Datagram(DataType.ACK.getValue(), 32, datagram.getCurPkt(), 1);
|
||||
packet = new DatagramPacket(responseDatagram.getPayload(), responseDatagram.getLength(), address, port);
|
||||
socket.send(packet);
|
||||
|
||||
printSummary();
|
||||
statistics.tick();
|
||||
if(ackEnd && statistics.getPacketsTransferredTotal() > datagram.getMaxPkt()) {
|
||||
running = false;
|
||||
statistics.summary();
|
||||
}
|
||||
|
||||
|
||||
lastSequence = thisSequence;
|
||||
pktsReceived++;
|
||||
pktsReceivedTotal++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue