// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.websocket.client.examples; import java.net.InetSocketAddress; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.common.OpCode; import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule; /** * This is not a general purpose websocket client. It's only for testing the websocket server and is hardwired to a specific draft version of the protocol. */ public class TestClient { public class TestSocket extends WebSocketAdapter { @Override public void onWebSocketBinary(byte[] payload, int offset, int len) { } @Override public void onWebSocketClose(int statusCode, String reason) { super.onWebSocketClose(statusCode,reason); } @Override public void onWebSocketConnect(Session session) { if (_verbose) { System.err.printf("%s#onWebSocketConnect %s %s\n",this.getClass().getSimpleName(),session,session.getClass().getSimpleName()); } } public void send(byte op, byte[] data, int maxFragmentLength) { _starts.add(System.nanoTime()); int off = 0; int len = data.length; if ((maxFragmentLength > 0) && (len > maxFragmentLength)) { len = maxFragmentLength; } __messagesSent++; while (off < data.length) { __framesSent++; off += len; if ((data.length - off) > len) { len = data.length - off; } if ((maxFragmentLength > 0) && (len > maxFragmentLength)) { len = maxFragmentLength; } } } } private static boolean _verbose = false; private static final Random __random = new Random(); private static LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("TestClient"); private final String _host; private final int _port; private final String _protocol; private final int _timeout; private static int __framesSent; private static int __messagesSent; private static AtomicInteger __framesReceived = new AtomicInteger(); private static AtomicInteger __messagesReceived = new AtomicInteger(); private static AtomicLong __totalTime = new AtomicLong(); private static AtomicLong __minDuration = new AtomicLong(Long.MAX_VALUE); private static AtomicLong __maxDuration = new AtomicLong(Long.MIN_VALUE); private static long __start; public static void main(String[] args) throws Exception { String host = "localhost"; int port = 8080; String protocol = null; int count = 10; int size = 64; int fragment = 4000; boolean binary = false; int clients = 1; int delay = 1000; for (int i = 0; i < args.length; i++) { String a = args[i]; if ("-p".equals(a) || "--port".equals(a)) { port = Integer.parseInt(args[++i]); } else if ("-h".equals(a) || "--host".equals(a)) { host = args[++i]; } else if ("-c".equals(a) || "--count".equals(a)) { count = Integer.parseInt(args[++i]); } else if ("-s".equals(a) || "--size".equals(a)) { size = Integer.parseInt(args[++i]); } else if ("-f".equals(a) || "--fragment".equals(a)) { fragment = Integer.parseInt(args[++i]); } else if ("-P".equals(a) || "--protocol".equals(a)) { protocol = args[++i]; } else if ("-v".equals(a) || "--verbose".equals(a)) { _verbose = true; } else if ("-b".equals(a) || "--binary".equals(a)) { binary = true; } else if ("-C".equals(a) || "--clients".equals(a)) { clients = Integer.parseInt(args[++i]); } else if ("-d".equals(a) || "--delay".equals(a)) { delay = Integer.parseInt(args[++i]); } else if (a.startsWith("-")) { usage(args); } } TestClient[] client = new TestClient[clients]; WebSocketClient wsclient = new WebSocketClient(bufferPool); try { wsclient.start(); __start = System.currentTimeMillis(); protocol = protocol == null?"echo":protocol; for (int i = 0; i < clients; i++) { client[i] = new TestClient(wsclient,host,port,protocol,60000); client[i].open(); } System.out.println("Jetty WebSocket PING " + host + ":" + port + " (" + new InetSocketAddress(host,port) + ") " + clients + " clients " + protocol); for (int p = 0; p < count; p++) { long next = System.currentTimeMillis() + delay; byte op = OpCode.TEXT; if (binary) { op = OpCode.BINARY; } byte data[] = null; switch (op) { case OpCode.TEXT: { StringBuilder b = new StringBuilder(); while (b.length() < size) { b.append('A' + __random.nextInt(26)); } data = b.toString().getBytes(StandardCharsets.UTF_8); break; } case OpCode.BINARY: { data = new byte[size]; __random.nextBytes(data); break; } } for (int i = 0; i < clients; i++) { client[i].send(op,data,fragment); } while (System.currentTimeMillis() < next) { Thread.sleep(10); } } } finally { for (int i = 0; i < clients; i++) { if (client[i] != null) { client[i].disconnect(); } } long duration = System.currentTimeMillis() - __start; System.out.println("--- " + host + " websocket ping statistics using " + clients + " connection" + (clients > 1?"s":"") + " ---"); System.out.printf("%d/%d frames sent/recv, %d/%d mesg sent/recv, time %dms %dm/s %.2fbps%n",__framesSent,__framesReceived.get(),__messagesSent, __messagesReceived.get(),duration,((1000L * __messagesReceived.get()) / duration),(1000.0D * __messagesReceived.get() * 8 * size) / duration / 1024 / 1024); System.out.printf("rtt min/ave/max = %.3f/%.3f/%.3f ms\n",__minDuration.get() / 1000000.0,__messagesReceived.get() == 0?0.0:(__totalTime.get() / __messagesReceived.get() / 1000000.0),__maxDuration.get() / 1000000.0); wsclient.stop(); } bufferPool.assertNoLeaks(); } private static void usage(String[] args) { System.err.println("ERROR: " + Arrays.asList(args)); System.err.println("USAGE: java -cp CLASSPATH " + TestClient.class + " [ OPTIONS ]"); System.err.println(" -h|--host HOST (default localhost)"); System.err.println(" -p|--port PORT (default 8080)"); System.err.println(" -b|--binary"); System.err.println(" -v|--verbose"); System.err.println(" -c|--count n (default 10)"); System.err.println(" -s|--size n (default 64)"); System.err.println(" -f|--fragment n (default 4000) "); System.err.println(" -P|--protocol echo|echo-assemble|echo-fragment|echo-broadcast"); System.err.println(" -C|--clients n (default 1) "); System.err.println(" -d|--delay n (default 1000ms) "); System.exit(1); } private BlockingQueue<Long> _starts = new LinkedBlockingQueue<Long>(); int _messageBytes; int _frames; byte _opcode = -1; private WebSocketClient client; private TestSocket socket; public TestClient(WebSocketClient client, String host, int port, String protocol, int timeoutMS) throws Exception { this.client = client; _host = host; _port = port; _protocol = protocol; _timeout = timeoutMS; } private void disconnect() { // TODO Auto-generated method stub } private void open() throws Exception { client.getPolicy().setIdleTimeout(_timeout); ClientUpgradeRequest request = new ClientUpgradeRequest(); request.setSubProtocols(_protocol); socket = new TestSocket(); URI wsUri = new URI("ws://" + _host + ":" + _port + "/"); client.connect(socket,wsUri,request).get(10,TimeUnit.SECONDS); } private void send(byte op, byte[] data, int fragment) { socket.send(op,data,fragment); } }