/******************************************************************************* * Copyright (c) 2015 Institute for Pervasive Computing, ETH Zurich and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.html. * * Contributors: * Matthias Kovatsch - creator and main architect * Martin Lanter - architect and initial implementation ******************************************************************************/ package org.eclipse.californium.tools; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.Socket; import java.net.SocketException; import java.net.URI; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.NoSuchElementException; import java.util.Scanner; import org.eclipse.californium.tools.coapbench.Command; import org.eclipse.californium.tools.coapbench.VirtualDeviceManager; /** * The client slave keeps a TCP connection to the master. The master sends * commands to the slave. */ public class ClientSlave { public static final String CMD_PING = "ping"; public static final String CMD_EXIT = "exit"; public static final String CMD_STRESS = "stress"; public static final String CMD_BENCH = "bench"; public static final String CMD_OBSERVE_BENCH = "observe"; public static final String CMD_OBSERVE_START = "observe_start"; public static final String CMD_OBSERVE_READY = "observe_ready"; public static final String CMD_OBSERVE_FAIL = "observe_fail"; public static final String CMD_APACHE_BENCH = "ab"; private InetAddress address; private int port; private Socket socket; private boolean verbose; private VirtualDeviceManager vdm; private ApacheBench ab; public ClientSlave(InetAddress address, int port) throws Exception { this.address = address; this.port = port; System.out.println("Start client slave"); } public void start() { try { while (true) { connect(); runrun(); } } catch (Exception e) { e.printStackTrace(); } finally { // Something went wrong. We should not leave the method run() System.err.println("SEVERE: Client has stopped working"); } } private void connect() { // Try to connect until it worked while (true) { try { socket = new Socket( address, port); System.out.println("Connected to "+socket.getRemoteSocketAddress()); return; // return if successful } catch (Exception e) { System.err.println("Failed to connect to "+address+":"+port); try { Thread.sleep(1000); } catch (InterruptedException ie) { e.printStackTrace(); } } } } public void runrun() { System.out.println("Waiting for commands"); Scanner in = null; try { socket.setSoTimeout(0); in = new Scanner(socket.getInputStream()); while (true) { String command = in.nextLine(); System.out.println("\nReceived command: "+command); if (command.startsWith(CMD_PING)) { send(CMD_PING); // respond with ping } else if (command.startsWith(CMD_STRESS)) { stress(new Command(command)); } else if (command.startsWith(CMD_BENCH)) { bench(new Command(command)); } else if (command.startsWith(CMD_OBSERVE_START)) { observe_start(); } else if (command.startsWith(CMD_OBSERVE_FAIL)) { observe_fail(); } else if (command.startsWith(CMD_OBSERVE_BENCH)) { observe(new Command(command)); } else if (command.startsWith(CMD_APACHE_BENCH)) { ab(new Command(command)); } else if (command.startsWith(CMD_EXIT)) { System.exit(0); } else { System.out.println("Unknown command: "+command); } } } catch (NoSuchElementException e) { // When master is shutdown, we arrive here System.out.println("Exception when reading from master: \""+e.getMessage()+"\""); } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) in.close(); } } public void send(String response) { try { socket.getOutputStream().write(new String(response + "\n").getBytes()); socket.getOutputStream().flush(); } catch (Exception e) { e.printStackTrace(); } } private void stress(Command command) throws Exception { if (command.has("start")) StressClient.main(null); if (command.has("stop")) StressClient.stop(); } private void bench(Command command) throws Exception { if (this.vdm == null) { this.vdm = new VirtualDeviceManager(); this.vdm.setVerbose(verbose); } int clients = CoapBench.DEFAULT_CLIENTS; int time = CoapBench.DEFAULT_TIME; if (command.has("-c")) clients = command.getInt("-c"); if (command.has("-t")) time = command.getInt("-t"); if (command.has("-latency")) vdm.setEnableLatency(true); List<String> parameters = command.getParameters(); if (parameters.size() > 0) { URI uri = new URI(parameters.get(0)); vdm.setURI(uri); vdm.start(clients, time * 1000); } else if (command.has("-new-log")) { vdm.lognew(command.getString("-new-log")); } else if (command.has("-log")) { vdm.log(command.getString("-log")); } else { System.err.println("Error: No target specified"); } } private void observe(Command command) throws Exception { InetSocketAddress targetAddr = null; if (this.vdm == null) { this.vdm = new VirtualDeviceManager(); this.vdm.setVerbose(true); } int servers = CoapBench.DEFAULT_CLIENTS; int time = CoapBench.DEFAULT_TIME; if (command.has("-s")) servers = command.getInt("-s"); if (command.has("-t")) time = command.getInt("-t"); if (command.has("-non")) vdm.setConfirmable(false); if (command.has("-latency")) vdm.setEnableLatency(true); List<String> parameters = command.getParameters(); if (parameters.size() > 0) { // if there's another test currently running, we stop it if (vdm.isRunning()) vdm.stop(); // setup the benchmark target URI uri = new URI(parameters.get(0)); vdm.setURI(uri); targetAddr = findSuitableAddress(uri.getHost()); if (targetAddr == null) { System.out.println("Could not find a suitable interface to establish the connection; falling back to localhost."); vdm.setBindAddress(null); } else if (!targetAddr.equals(vdm.getBindAddress())) { vdm.setBindAddress(targetAddr); } // begin operation (spawning devices, registration) vdm.start(servers, time * 1000, false); // wait for all the servers to finish registering with the observer int i = 0; for (; i < 80; ++i) { Thread.sleep(250); if (vdm.getNumberOfDevicesAtBarrier() == servers) break; } if (i < 80) { send(CMD_OBSERVE_READY); } else { System.err.println("\nNot all virtual servers (" + servers + ") were able to initialize (number of servers ready: " + vdm.getNumberOfDevicesAtBarrier() + ")!"); send(CMD_OBSERVE_FAIL); vdm.stop(); } } else if (command.has("-new-log")) { vdm.lognew(command.getString("-new-log")); } else if (command.has("-log")) { vdm.log(command.getString("-log")); } else { System.err.println("Error: No target specified"); } } private void observe_start() throws Exception { if (vdm == null || vdm.getDeviceCount() == 0) { System.err.println("Observe benchmark error: Test hasn't been defined yet."); return; } if (vdm.getNumberOfDevicesAtBarrier() == vdm.getDeviceCount()) vdm.joinBarrier(); else throw new Exception("Premature test trigger (ready are " + vdm.getNumberOfDevicesAtBarrier() + "/" + vdm.getDeviceCount() + " initialized devices); check the master's status."); } private void observe_fail() throws Exception { if (vdm != null) vdm.stop(); } private void ab(Command command) throws Exception { if (this.ab == null) this.ab = new ApacheBench(); ab.start(command); } /* This method is used to find an interface, to the address of which we can bind virtual devices' sockets */ private InetSocketAddress findSuitableAddress(String hostURI) { InetSocketAddress retAddr = null; Enumeration<NetworkInterface> nets; InetSocketAddress hostAddr = null; byte[] inaddr, hostinaddr, mask; hostAddr = new InetSocketAddress(hostURI, 0); hostinaddr = hostAddr.getAddress().getAddress(); try { nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface netint : Collections.list(nets)) { for (InterfaceAddress ia : netint.getInterfaceAddresses()) { if (ia.getAddress().getAddress().length != hostAddr.getAddress().getAddress().length || ia.getAddress().isLoopbackAddress()) continue; inaddr = ia.getAddress().getAddress(); mask = new byte[inaddr.length]; int maskbyte = 0; for (; (maskbyte + 1) * 8 < ia.getNetworkPrefixLength(); ++maskbyte) { mask[maskbyte] = (byte)0xFF; } mask[maskbyte] = (byte)((int)(Math.pow(2.0, (ia.getNetworkPrefixLength() - maskbyte * 8)) - 1) << (8 - ia.getNetworkPrefixLength() + maskbyte * 8)); int maskcounter = 0; for (; maskcounter * 8 < ia.getNetworkPrefixLength(); ++maskcounter) { if ((inaddr[maskcounter] & mask[maskcounter]) != (hostinaddr[maskcounter] & mask[maskcounter])) break; } if (maskcounter * 8 < ia.getNetworkPrefixLength()) continue; retAddr = new InetSocketAddress(ia.getAddress(), 0); return retAddr; } } } catch (SocketException se) { se.printStackTrace(); } return retAddr; } public static void main(String[] args) throws Exception { InetAddress address = InetAddress.getByName(CoapBench.DEFAULT_MASTER_ADDRESS); new ClientSlave(address, CoapBench.DEFAULT_MASTER_PORT).start(); } public boolean isVerbose() { return verbose; } public void setVerbose(boolean verbose) { this.verbose = verbose; } }