/* * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ /* * * * An "echo" service designed to be used with inetd. It can be configured in * inetd.conf to be used by any of the following types of services :- * * stream tcp nowait * stream tcp6 nowait * stream tcp wait * stream tcp6 wait * dgram udp wait * dgram udp6 wait * * If configured as a "tcp nowait" service then inetd will launch a * VM to run the EchoService each time that a client connects to * the TCP port. The EchoService simply echos any messages it * receives from the client and shuts if the client closes the * connection. * * If configured as a "tcp wait" service then inetd will launch a VM * to run the EchoService when a client connects to the port. When * launched the EchoService takes over the listener socket. It * terminates when all clients have disconnected and the service * is idle for a few seconds. * * If configured as a "udp wait" service then a VM will be launched for * each UDP packet to the configured port. System.inheritedChannel() * will return a DatagramChannel. The echo service here will terminate after * echoing the UDP packet back to the client. * * The service closes the inherited network channel when complete. To * facilate testing that the channel is closed the "tcp nowait" service * can close the connection after a given number of bytes. */ import java.nio.*; import java.nio.channels.*; import java.io.IOException; import java.net.*; public class EchoService { private static void doIt(SocketChannel sc, int closeAfter, int delay) throws IOException { ByteBuffer bb = ByteBuffer.allocate(1024); int total = 0; for (;;) { bb.clear(); int n = sc.read(bb); if (n < 0) { break; } total += n; // echo bb.flip(); sc.write(bb); // close after X bytes? if (closeAfter > 0 && total >= closeAfter) { break; } } sc.close(); if (delay > 0) { try { Thread.currentThread().sleep(delay); } catch (InterruptedException x) { } } } private static void doIt(DatagramChannel dc) throws IOException { ByteBuffer bb = ByteBuffer.allocate(1024); SocketAddress sa = dc.receive(bb); bb.flip(); dc.send(bb, sa); dc.close(); } // A worker thread to service a single connection // The class maintains a count of the number of worker threads so // can the service can terminate then all clients disconnect. static class Worker implements Runnable { private static int count = 0; private static Object lock = new Object(); public static int count() { synchronized (lock) { return count; } } private SocketChannel sc; Worker(SocketChannel sc) { this.sc = sc; synchronized (lock) { count++; } } public void run() { try { doIt(sc, -1, -1); } catch (IOException x) { } finally { synchronized (lock) { count--; } } } } public static void main(String args[]) throws IOException { Channel c = System.inheritedChannel(); if (c == null) { return; } // tcp nowait if (c instanceof SocketChannel) { int closeAfter = 0; int delay = 0; if (args.length > 0) { closeAfter = Integer.parseInt(args[0]); } if (args.length > 1) { delay = Integer.parseInt(args[1]); } doIt((SocketChannel)c, closeAfter, delay); } // tcp wait - in this case we take over the listener socket // In this test case we create a thread to service each connection // and terminate after all clients are gone. // if (c instanceof ServerSocketChannel) { ServerSocketChannel ssc = (ServerSocketChannel)c; ssc.configureBlocking(false); Selector sel = ssc.provider().openSelector(); SelectionKey sk = ssc.register(sel, SelectionKey.OP_ACCEPT); SocketChannel sc; int count = 0; for (;;) { sel.select(5000); if (sk.isAcceptable() && ((sc = ssc.accept()) != null)) { Worker w = new Worker(sc); (new Thread(w)).start(); } else { // if all clients have disconnected then we die as well. if (Worker.count() == 0) { break; } } } ssc.close(); } // udp wait if (c instanceof DatagramChannel) { doIt((DatagramChannel)c); } // linger? if (args.length > 0) { int delay = Integer.parseInt(args[0]); try { Thread.currentThread().sleep(delay); } catch (InterruptedException x) { } } } }