/* Copyright (c) 2006, Sriram Srinivasan
*
* You may distribute this software under the terms of the license
* specified in the file "License"
*/
package kilim.test;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import junit.framework.TestCase;
import kilim.Pausable;
import kilim.Scheduler;
import kilim.nio.EndPoint;
import kilim.nio.NioSelectorScheduler;
import kilim.nio.SessionTask;
public class TestIO extends TestCase {
static final int ITERS = 10;
static final int NCLIENTS = 100;
NioSelectorScheduler nio;
int port;
@Override
protected void setUp() throws Exception {
nio = new NioSelectorScheduler(); // Starts a single thread that manages the select loop
port = nio.listen(0, EchoServer.class, Scheduler.getDefaultScheduler()); //
}
@Override
protected void tearDown() throws Exception {
nio.shutdown();
Thread.sleep(500); // Allow the socket to be closed
}
/**
* Launch many ping clients, each of which is paired with its own instance of {@link EchoServer}.
* @throws IOException
*/
public void testParallelEchoes() throws IOException {
try {
for (int i = 0; i < NCLIENTS; i++) {
client(port);
}
} catch (IOException e) {
e.printStackTrace();
fail("IOException " + e.getClass() + ":" + e.getMessage());
}
}
public void testDelay() throws IOException {
SocketChannel sc = SocketChannel.open();
try {
sc.socket().connect(new InetSocketAddress("localhost", port));
String s = "Iteration #0. DONE"; // Only because EchoServer checks for it.
byte[] sbytes = s.getBytes();
ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(sbytes.length);
dos.write(sbytes);
byte[] sendbuf = baos.toByteArray();
// Now write the bytes in little dribs and drabs and delaying in between. This tests fill's yield.
OutputStream os = sc.socket().getOutputStream();
sendChunkWithDelay(os, sendbuf, 0, 1); // splitting the length prefix
sendChunkWithDelay(os, sendbuf, 1, 2);
sendChunkWithDelay(os, sendbuf, 3, 4);
sendChunkWithDelay(os, sendbuf, 7, 3);
sendChunkWithDelay(os, sendbuf, 10, sendbuf.length - 10); // the rest
// Ideally, would like to simulate flow control on the rcv end as well, but would have to turn off
// socket buffering on the EchoServer side of things.
String rs = rcv(sc);
assertEquals(s, rs);
} catch (IOException e) {
e.printStackTrace();
fail("IOException " + e.getClass() + ":" + e.getMessage());
}
}
public void sendChunkWithDelay(OutputStream os, byte[] sendbuf, int offset, int len) throws IOException {
os.write(sendbuf, offset, len);
os.flush();
try {Thread.sleep(100);} catch (InterruptedException ignore) {}
}
/**
* test client side utility function. uses blocking Java I/O to send a length-prefixed string.
*/
static void send(SocketChannel sc, String s) throws IOException {
byte[] bytes = s.getBytes();
int len = bytes.length;
DataOutputStream dos = new DataOutputStream(sc.socket().getOutputStream());
dos.writeInt(len);
dos.write(bytes);
dos.flush();
}
/**
* test client side utility function. uses blocking Java I/O to rcv a length-prefixed string.
*/
static String rcv(SocketChannel sc) throws IOException {
// rcv
DataInputStream dis = new DataInputStream(sc.socket().getInputStream());
byte[] bytes = new byte[100];
int len = dis.readInt();
assertTrue(len < bytes.length);
int offset = 0;
while (len > 0) {
int n = dis.read(bytes, offset, len);
if (n == -1) {
throw new IOException("Unexpected termination");
}
len -= n;
offset += n;
}
return new String(bytes, 0, offset); // offset contains the length.
}
static void client(int port) throws IOException {
SocketChannel sc = SocketChannel.open();
try {
// Client using regular JDK I/O API.
sc.socket().connect(new InetSocketAddress("localhost", port));
for (int i = 0 ; i < ITERS; i++) {
String s = "Iteration #" + i;
if (i == ITERS-1) {s += " DONE";}
send(sc, s);
String rs = rcv(sc);
assertEquals(s, rs);
}
} finally {
sc.close();
}
}
public static class EchoServer extends SessionTask {
@Override
public void execute() throws Pausable, Exception {
ByteBuffer buf = ByteBuffer.allocate(100);
EndPoint ep = getEndPoint();
while (true) {
buf.clear();
buf = ep.fillMessage(buf, 4, /*lengthIncludesItself*/ false);
buf.flip();
int strlen = buf.getInt();
String s= new String(buf.array(), 4, strlen);
//System.out.println ("Rcvd: " + s);
if (!s.startsWith("Iteration #")) {
ep.close();
break;
}
buf.position(0); // reset read pos
ep.write(buf); // echo.
if (s.endsWith("DONE")) {
ep.close();
break;
}
}
}
}
}