blob: 9e8146cac53cdd3d9db95579bffa7cbae74009f6 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 *
26 *
27 * An "echo" service designed to be used with inetd. It can be configured in
28 * inetd.conf to be used by any of the following types of services :-
29 *
30 * stream tcp nowait
31 * stream tcp6 nowait
32 * stream tcp wait
33 * stream tcp6 wait
34 * dgram udp wait
35 * dgram udp6 wait
36 *
37 * If configured as a "tcp nowait" service then inetd will launch a
38 * VM to run the EchoService each time that a client connects to
39 * the TCP port. The EchoService simply echos any messages it
40 * receives from the client and shuts if the client closes the
41 * connection.
42 *
43 * If configured as a "tcp wait" service then inetd will launch a VM
44 * to run the EchoService when a client connects to the port. When
45 * launched the EchoService takes over the listener socket. It
46 * terminates when all clients have disconnected and the service
47 * is idle for a few seconds.
48 *
49 * If configured as a "udp wait" service then a VM will be launched for
50 * each UDP packet to the configured port. System.inheritedChannel()
51 * will return a DatagramChannel. The echo service here will terminate after
52 * echoing the UDP packet back to the client.
53 *
54 * The service closes the inherited network channel when complete. To
55 * facilate testing that the channel is closed the "tcp nowait" service
56 * can close the connection after a given number of bytes.
57 */
58import java.nio.*;
59import java.nio.channels.*;
60import java.io.IOException;
61import java.net.*;
62
63public class EchoService {
64
65 private static void doIt(SocketChannel sc, int closeAfter, int delay) throws IOException {
66 ByteBuffer bb = ByteBuffer.allocate(1024);
67 int total = 0;
68 for (;;) {
69 bb.clear();
70 int n = sc.read(bb);
71 if (n < 0) {
72 break;
73 }
74 total += n;
75
76 // echo
77 bb.flip();
78 sc.write(bb);
79
80 // close after X bytes?
81 if (closeAfter > 0 && total >= closeAfter) {
82 break;
83 }
84 }
85
86 sc.close();
87 if (delay > 0) {
88 try {
89 Thread.currentThread().sleep(delay);
90 } catch (InterruptedException x) { }
91 }
92 }
93
94 private static void doIt(DatagramChannel dc) throws IOException {
95 ByteBuffer bb = ByteBuffer.allocate(1024);
96 SocketAddress sa = dc.receive(bb);
97 bb.flip();
98 dc.send(bb, sa);
99 dc.close();
100 }
101
102
103 // A worker thread to service a single connection
104 // The class maintains a count of the number of worker threads so
105 // can the service can terminate then all clients disconnect.
106
107 static class Worker implements Runnable {
108 private static int count = 0;
109 private static Object lock = new Object();
110
111 public static int count() {
112 synchronized (lock) {
113 return count;
114 }
115 }
116
117 private SocketChannel sc;
118
119 Worker(SocketChannel sc) {
120 this.sc = sc;
121 synchronized (lock) {
122 count++;
123 }
124 }
125
126 public void run() {
127 try {
128 doIt(sc, -1, -1);
129 } catch (IOException x) {
130 } finally {
131 synchronized (lock) {
132 count--;
133 }
134 }
135
136 }
137 }
138
139 public static void main(String args[]) throws IOException {
140 Channel c = System.inheritedChannel();
141 if (c == null) {
142 return;
143 }
144
145 // tcp nowait
146 if (c instanceof SocketChannel) {
147 int closeAfter = 0;
148 int delay = 0;
149 if (args.length > 0) {
150 closeAfter = Integer.parseInt(args[0]);
151 }
152 if (args.length > 1) {
153 delay = Integer.parseInt(args[1]);
154 }
155 doIt((SocketChannel)c, closeAfter, delay);
156 }
157
158 // tcp wait - in this case we take over the listener socket
159 // In this test case we create a thread to service each connection
160 // and terminate after all clients are gone.
161 //
162 if (c instanceof ServerSocketChannel) {
163 ServerSocketChannel ssc = (ServerSocketChannel)c;
164
165 ssc.configureBlocking(false);
166 Selector sel = ssc.provider().openSelector();
167 SelectionKey sk = ssc.register(sel, SelectionKey.OP_ACCEPT);
168 SocketChannel sc;
169 int count = 0;
170 for (;;) {
171 sel.select(5000);
172 if (sk.isAcceptable() && ((sc = ssc.accept()) != null)) {
173 Worker w = new Worker(sc);
174 (new Thread(w)).start();
175 } else {
176 // if all clients have disconnected then we die as well.
177 if (Worker.count() == 0) {
178 break;
179 }
180 }
181 }
182 ssc.close();
183 }
184
185 // udp wait
186 if (c instanceof DatagramChannel) {
187 doIt((DatagramChannel)c);
188 }
189
190 // linger?
191 if (args.length > 0) {
192 int delay = Integer.parseInt(args[0]);
193 try {
194 Thread.currentThread().sleep(delay);
195 } catch (InterruptedException x) { }
196 }
197
198 }
199
200}