package org.limewire.rudp; import java.io.BufferedReader; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.charset.Charset; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import org.limewire.concurrent.ExecutorsHelper; import org.limewire.concurrent.ManagedThread; import org.limewire.io.IOUtils; import org.limewire.listener.AsynchronousMulticasterImpl; import org.limewire.listener.EventListenerList; import org.limewire.nio.AbstractNBSocket; import org.limewire.nio.NBSocket; import org.limewire.nio.NIODispatcher; import org.limewire.nio.observer.ConnectObserver; import org.limewire.rudp.messages.RUDPMessageFactory; import org.limewire.rudp.messages.SynMessage.Role; import org.limewire.rudp.messages.impl.DefaultMessageFactory; import org.limewire.util.BaseTestCase; import org.limewire.util.StringUtils; import junit.framework.Test; /** * Put full UDPConnection system through various tests. */ public final class UDPConnectionTest extends BaseTestCase { private static final int TIMEOUT = 10 * 1000; private volatile UDPServiceStub stubService; private volatile UDPMultiplexor udpMultiplexor; private volatile UDPSelectorProvider udpSelectorProvider; private volatile AbstractNBSocket uconn1; private volatile AbstractNBSocket uconn2; private RUDPContext context; /* * Constructs the test. */ public UDPConnectionTest(String name) { super(name); } public static Test suite() { return buildTestSuite(UDPConnectionTest.class); } /** * Runs this test individually. */ public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } @Override public void setUp() throws Exception { RUDPMessageFactory factory = new DefaultMessageFactory(); stubService = new UDPServiceStub(factory); context = new DefaultRUDPContext( factory, NIODispatcher.instance().getTransportListener(), stubService, new DefaultRUDPSettings()); Executor executor = ExecutorsHelper.newProcessingQueue("TestEventThread"); udpSelectorProvider = new UDPSelectorProvider(context, new AsynchronousMulticasterImpl<UDPSocketChannelConnectionEvent>(executor)); udpMultiplexor = udpSelectorProvider.openSelector(); stubService.setUDPMultiplexor(udpMultiplexor); NIODispatcher.instance().registerSelector(udpMultiplexor, udpSelectorProvider.getUDPSocketChannelClass()); // Add some simulated connections to the UDPServiceStub stubService.addReceiver(6346, 6348, 10, 0); stubService.addReceiver(6348, 6346, 10, 0); } @Override public void tearDown() throws Exception { if (uconn1 != null) { uconn1.shutdown(); } if (uconn2 != null) { uconn2.shutdown(); } // Clear out the receiver parameters for the UDPServiceStub stubService.clearReceivers(); NIODispatcher.instance().removeSelector(udpMultiplexor); } /** * Test that data can be written, echoed and read through the * UDPConnections. * * @throws Exception if an error occurs */ public void testBasics() throws Exception { final int NUM_BYTES = 20000; final CountDownLatch threadEnder = new CountDownLatch(1); // Start the second connection in another thread // and run it to completion. class Inner extends ManagedThread { @Override public void run() { try { uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6348)); UStandalone.echoServer(uconn2, NUM_BYTES); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); t.setName("EchoServer"); try { t.start(); // Init the first connection uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6346)); // Run the first connection UStandalone.echoClient(uconn1, NUM_BYTES); } finally { // Wait for the second to finish assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } public void testBlockTransfers() throws Exception { final int NUM_BLOCKS = 100; // Start the second connection in another thread // and run it to completion. final CountDownLatch threadEnder = new CountDownLatch(1); class Inner extends ManagedThread { @Override public void run() { try { uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6348)); UStandalone.echoServerBlock(uconn2, NUM_BLOCKS); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try{ t.start(); // Init the first connection uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6346)); // Run the first connection UStandalone.echoClientBlock(uconn1, NUM_BLOCKS); } finally { // Wait for the second to finish assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Test that transfers data from a sender to a receiver, comparing 4 bytes at a time. */ public void testOneWayTransfer() throws Exception { final int MAX_VALUE = 1 * 1000 * 1000; // Clear out my standard setup stubService.clearReceivers(); // Add some simulated connections to the UDPServiceStub // Make the connections 5% flaky stubService.addReceiver(6346, 6348, 1, 0); stubService.addReceiver(6348, 6346, 1, 0); final CountDownLatch threadEnder = new CountDownLatch(1); // start the first connection in another thread class Inner extends ManagedThread { @Override public void run() { try { uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6348), 5000); uconn1.setSoTimeout(TIMEOUT); InputStream istream = uconn1.getInputStream(); for (int i = 0; i < MAX_VALUE; i++) { int rval = readInt(istream); assertEquals("Unexpected data at offset: " + i, Integer.toHexString(i), Integer.toHexString(rval)); } } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try { t.start(); // start the second connection uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6346), 5000); uconn2.setSoTimeout(TIMEOUT); OutputStream ostream = uconn2.getOutputStream(); for (int i = 0; i < MAX_VALUE; i++) { writeInt(i, ostream); } } finally { assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } public void testIOUtilsOnStream() throws Exception { // initialize connection ConnStarter starter = new ConnStarter(); starter.connect(); // Output on the first connection OutputStream ostream = uconn1.getOutputStream(); ostream.write(StringUtils.toAsciiBytes("GET FOO BAR BLECK\r\nSecond Line\r\n")); //uconn1.close(); // Read to end and one extra on second stream InputStream istream = uconn2.getInputStream(); uconn2.setSoTimeout(TIMEOUT); String word = IOUtils.readWord(istream,8); assertEquals("GET", word); } public void testBufferedByteReader() throws Exception { String line1 = "GET FOO BAR BLECK"; String line2 = "Second Line"; // initialize connection ConnStarter starter = new ConnStarter(); starter.connect(); // Output on the first connection OutputStream ostream = uconn1.getOutputStream(); ostream.write(StringUtils.toAsciiBytes(line1 + "\r\n" + line2 + "\r\n")); // Read to end and one extra on second stream InputStream istream = uconn2.getInputStream(); uconn2.setSoTimeout(TIMEOUT); BufferedReader br = new BufferedReader(new InputStreamReader(istream, Charset.forName("US-ASCII"))); String line = br.readLine(); assertEquals(line1, line); } public void testReadBeyondEnd() throws Exception { final int NUM_BYTES = 100; // initialize connection ConnStarter starter = new ConnStarter(); starter.connect(); // Output on the first connection OutputStream ostream = uconn1.getOutputStream(); for ( int i = 0; i < NUM_BYTES; i++ ) ostream.write(i % 256); // Read to end and one extra on second stream InputStream istream = uconn2.getInputStream(); int rval; for ( int i = 0; i < NUM_BYTES; i++ ) { rval = istream.read(); if ( (i % 256) != rval ) fail("Error on byte:"+i); } // Close writer uconn1.close(); // Read from reader rval = istream.read(); // Validate the results assertEquals("Read at end of stream should be -1", rval, -1); } public void testReadBeyondEndAsBlock() throws Exception { final int NUM_BYTES = 100; // initialize connection ConnStarter starter = new ConnStarter(); starter.connect(); // Output on the first connection OutputStream ostream = uconn1.getOutputStream(); for ( int i = 0; i < NUM_BYTES; i++ ) ostream.write(i % 256); // Let data get sent to reader Thread.sleep(500); // Close writer uconn1.close(); // Read to end and one extra on second stream InputStream istream = uconn2.getInputStream(); byte bdata[] = new byte[512]; int rval; int i = 0; while (true) { int len = istream.read(bdata); for ( int j = 0; j < len; j++ ) { rval = bdata[j] & 0xff; if ( (i % 256) != rval ) fail("Error on byte:"+i); i++; } if (i >= NUM_BYTES) break; } // Read from reader rval = istream.read(bdata); // Validate the results assertEquals("Read at end of stream should be -1", rval, -1); } public void testReadBeyondEndAsBlockDuringRead() throws Exception { final int NUM_BYTES = 100; // initialize connection ConnStarter starter = new ConnStarter(); starter.connect(); // Output on the first connection OutputStream ostream = uconn1.getOutputStream(); for ( int i = 0; i < NUM_BYTES; i++ ) ostream.write(i % 256); final CountDownLatch threadEnder = new CountDownLatch(1); // Close the writer while the reader is blocked class Inner extends ManagedThread { @Override public void run() { try { // Let reader lock up on block read Thread.sleep(500); // Close writer uconn1.close(); } catch(InterruptedException ie) { } finally { threadEnder.countDown(); } } } Inner st = new Inner(); st.setDaemon(true); try { st.start(); // Read to end and one extra on second stream InputStream istream = uconn2.getInputStream(); byte bdata[] = new byte[512]; int rval; int i = 0; while (true) { int len = istream.read(bdata); for ( int j = 0; j < len; j++ ) { rval = bdata[j] & 0xff; if ( (i % 256) != rval ) fail("Error on byte:"+i); i++; } if (i >= NUM_BYTES) break; } // Read from reader rval = istream.read(bdata); // Validate the results assertEquals("Read at end of stream should be -1", rval, -1); } finally { assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Test that data can be written, echoed and read through * UDPConnections. */ public void testConnection() throws Exception { final int NUM_BYTES = 10 * 1000 * 1000; // Clear out my standard setup stubService.clearReceivers(); // Add some simulated connections to the UDPServiceStub stubService.addReceiver(6346, 6348, 0, 0); stubService.addReceiver(6348, 6346, 0, 0); final CountDownLatch threadEnder = new CountDownLatch(1); // start the first connection in another thread class Inner extends ManagedThread { @Override public void run() { try { uconn1 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6348), 2000); uconn1.setSoTimeout(TIMEOUT); UStandalone.echoServer(uconn1, NUM_BYTES); } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try { t.start(); // start the second connection uconn2 = udpSelectorProvider.openSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6346), 2000); uconn2.setSoTimeout(TIMEOUT); UStandalone.echoClient(uconn2, NUM_BYTES); } finally { assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Tests if two connections initiated from the same side are routed * correctly. This means two server connections and two client connections * where the clients both use the same port and the servers both use the * same port. Routing should work because of connection ids. */ public void testTwoConnectionsFromOneSide() throws Exception { final int NUM_BYTES = 10 * 1000; // clear out routes from setUp() stubService.clearReceivers(); // Add some routes to the UDPServiceStub stubService.addReceiver(6346, 6348, 0, 0); stubService.addReceiver(6348, 6346, 0, 0); final CountDownLatch threadEnder = new CountDownLatch(4); /** * The server writes NUM_BYTES out. */ class Server extends ManagedThread { @Override public void run() { Socket socket = udpSelectorProvider.openAcceptorSocketChannel().socket(); try { socket.connect(new InetSocketAddress("127.0.0.1", 6348), 2000); socket.setSoTimeout(TIMEOUT); OutputStream out = socket.getOutputStream(); for (int i = 0; i < NUM_BYTES; i++) { out.write(i % 10); } } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Server t1 = new Server(); t1.setDaemon(true); Server t2 = new Server(); t2.setDaemon(true); t1.start(); t2.start(); /** * Client reads NUM_BYTES from the inputstream. */ class Client extends ManagedThread { @Override public void run() { try { // start the second connection Socket socket = udpSelectorProvider.openSocketChannel().socket(); socket.connect(new InetSocketAddress("127.0.0.1", 6346), 2000); socket.setSoTimeout(TIMEOUT); InputStream in = socket.getInputStream(); for (int i = 0; i < NUM_BYTES; i++) { int read = in.read(); assertEquals("read so far: " + i, i % 10, read); } } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Client c1 = new Client(); c1.setDaemon(true); Client c2 = new Client(); c2.setDaemon(true); c1.start(); c2.start(); assertTrue(threadEnder.await(1000 * 60 * 2, TimeUnit.MILLISECONDS)); } /** * Tests if two connections initiated from both side are routed * correctly. This means two server connections and two client connections * where each pair of client, server uses the same port. */ public void testTwoConnectionsFromBothSides() throws Exception { final int NUM_BYTES = 10 * 1000; // clear out routes from setUp() stubService.clearReceivers(); // Add some routes to the UDPServiceStub stubService.addReceiver(6346, 6348, 0, 0); stubService.addReceiver(6348, 6346, 0, 0); final CountDownLatch threadEnder = new CountDownLatch(4); /** * The server writes NUM_BYTES out. */ class Server extends ManagedThread { private final int port; public Server(int port) { this.port = port; } @Override public void run() { Socket socket = udpSelectorProvider.openAcceptorSocketChannel().socket(); try { socket.connect(new InetSocketAddress("127.0.0.1", port), 2000); socket.setSoTimeout(TIMEOUT); OutputStream out = socket.getOutputStream(); for (int i = 0; i < NUM_BYTES; i++) { out.write(i % 10); } } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } /** * Client reads NUM_BYTES from the inputstream. */ class Client extends ManagedThread { private final int port; public Client(int port) { this.port = port; } @Override public void run() { try { // start the second connection Socket socket = udpSelectorProvider.openSocketChannel().socket(); socket.connect(new InetSocketAddress("127.0.0.1", port), 2000); socket.setSoTimeout(TIMEOUT); InputStream in = socket.getInputStream(); for (int i = 0; i < NUM_BYTES; i++) { int read = in.read(); assertEquals("read so far: " + i, i % 10, read); } } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Server t1 = new Server(6346); t1.setDaemon(true); Server t2 = new Server(6348); t2.setDaemon(true); Client c1 = new Client(6346); c1.setDaemon(true); Client c2 = new Client(6348); c2.setDaemon(true); t1.start(); Thread.sleep(10); t2.start(); Thread.sleep(10); c1.start(); Thread.sleep(10); c2.start(); assertTrue(threadEnder.await(1000 * 60 * 2, TimeUnit.MILLISECONDS)); } /** * Ensures that the new acceptor code still accepts old syn messages coming * in, this is achieved by creating the UDPSocketChannel manually and specifying * its role as {@link Role#UNDEFINED}. */ public void testVersion1AcceptorAcceptsOldRequestors() throws Exception { final CountDownLatch connectLatch = new CountDownLatch(2); // clear default routes stubService.clearReceivers(); // Add routes to the UDPServiceStub stubService.addReceiver(6346, 6348, 0, 0); stubService.addReceiver(6348, 6346, 0, 0); NBSocket acceptorSocket = udpSelectorProvider.openAcceptorSocketChannel().socket(); acceptorSocket.connect(new InetSocketAddress("127.0.0.1", 6348), 2000, new ConnectObserver() { @Override public void handleConnect(Socket socket) throws IOException { connectLatch.countDown(); } @Override public void handleIOException(IOException iox) { } @Override public void shutdown() { } }); NBSocket requestorSocket = new UDPSocketChannel(udpSelectorProvider, context, Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()).socket(); requestorSocket.connect(new InetSocketAddress("127.0.0.1", 6346), 2000, new ConnectObserver() { @Override public void handleConnect(Socket socket) throws IOException { connectLatch.countDown(); } @Override public void handleIOException(IOException iox) { } @Override public void shutdown() { } }); assertTrue(connectLatch.await(2000, TimeUnit.MILLISECONDS)); } /** * Ensures that the new requestor code still accepts old syn messages coming * in, this is achieved by creating the UDPSocketChannel manually and specifying * its role as {@link Role#UNDEFINED}. This is not truly an old syn message * but it's role, but it's close enough. */ public void testVersion1RequestorConnectsToOldAcceptors() throws Exception { final CountDownLatch connectLatch = new CountDownLatch(2); // clear default routes stubService.clearReceivers(); // Add routes to the UDPServiceStub stubService.addReceiver(6346, 6348, 0, 0); stubService.addReceiver(6348, 6346, 0, 0); NBSocket acceptorSocket = udpSelectorProvider.openSocketChannel().socket(); acceptorSocket.connect(new InetSocketAddress("127.0.0.1", 6348), 2000, new ConnectObserver() { @Override public void handleConnect(Socket socket) throws IOException { connectLatch.countDown(); } @Override public void handleIOException(IOException iox) { } @Override public void shutdown() { } }); NBSocket requestorSocket = new UDPSocketChannel(udpSelectorProvider, context, Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>()).socket(); requestorSocket.connect(new InetSocketAddress("127.0.0.1", 6346), 2000, new ConnectObserver() { @Override public void handleConnect(Socket socket) throws IOException { connectLatch.countDown(); } @Override public void handleIOException(IOException iox) { } @Override public void shutdown() { } }); assertTrue(connectLatch.await(2000, TimeUnit.MILLISECONDS)); } /** * Test that data can be written, echoed and read through flaky * UDPConnections. * * @throws Exception if an error occurs */ public void testFlakyConnection() throws Exception { final int NUM_BYTES = 200000; // Clear out my standard setup stubService.clearReceivers(); // Add some simulated connections to the UDPServiceStub // Make the connections 5% flaky stubService.addReceiver(6346, 6348, 10, 5); stubService.addReceiver(6348, 6346, 10, 5); final CountDownLatch threadEnder = new CountDownLatch(1); // Start the second connection in another thread // and run it to completion. class Inner extends ManagedThread { @Override public void run() { try { uconn1 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6348), 2000); uconn1.setSoTimeout(TIMEOUT); UStandalone.echoServer(uconn1, NUM_BYTES); } catch (IOException e) { throw new RuntimeException(e); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try { t.start(); // Start the first connection uconn2 = udpSelectorProvider.openSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6346), 2000); uconn2.setSoTimeout(TIMEOUT); UStandalone.echoClient(uconn2, NUM_BYTES); } finally { // Wait for the second to finish assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Test that data can be written, echoed and read through * an extrely flaky UDPConnection where 15% of messages are lost. * * @throws Exception if an error occurs */ public void testExtremelyFlakyConnection() throws Exception { final int NUM_BYTES = 20000; // Clear out my standard setup stubService.clearReceivers(); // Add some simulated connections to the UDPServiceStub // Make the connections 15% flaky stubService.addReceiver(6346, 6348, 10, 10); stubService.addReceiver(6348, 6346, 10, 10); final CountDownLatch threadEnder = new CountDownLatch(1); // Start the second connection in another thread // and run it to completion. class Inner extends ManagedThread { @Override public void run() { try { uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6348), 2000); uconn2.setSoTimeout(TIMEOUT); UStandalone.echoServer(uconn2, NUM_BYTES); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try { t.start(); // Init the first connection uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6346), 2000); uconn1.setSoTimeout(TIMEOUT); // Run the first connection UStandalone.echoClient(uconn1, NUM_BYTES); } finally { // Wait for the second to finish assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Test UDPConnections with a very long delay. * * @throws Exception if an error occurs */ public void testExtremelySlowConnection() throws Exception { final int NUM_BYTES = 60000; // Clear out my standard setup stubService.clearReceivers(); // Add some simulated connections to the UDPServiceStub // Make the connections 25% flaky stubService.addReceiver(6346, 6348, 1000, 0); stubService.addReceiver(6348, 6346, 1000, 0); final CountDownLatch threadEnder = new CountDownLatch(1); // Start the second connection in another thread // and run it to completion. class Inner extends ManagedThread { @Override public void run() { try { uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6348), 10000); uconn2.setSoTimeout(TIMEOUT); UStandalone.echoServer(uconn2, NUM_BYTES); } catch (IOException ioe) { throw new RuntimeException(ioe); } finally { threadEnder.countDown(); } } } Inner t = new Inner(); t.setDaemon(true); try { t.start(); // Init the first connection uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6346), 10000); uconn1.setSoTimeout(TIMEOUT); // Run the first connection UStandalone.echoClient(uconn1, NUM_BYTES); } finally { // Wait for the second to finish assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } /** * Startup two connections. The second UDPConnection is started in a thread * since two connections will block if started in one thread. * <p> * Connections are assigned to fields in {@link UDPConnectionTest}. */ private class ConnStarter { public ConnStarter() { } public void connect() throws Exception { final CountDownLatch threadEnder = new CountDownLatch(1); Thread t = new ManagedThread(new Runnable() { public void run() { try { uconn2 = udpSelectorProvider.openAcceptorSocketChannel().socket(); uconn2.connect(new InetSocketAddress("127.0.0.1", 6348), 2000); } catch (IOException e) { fail("Error establishing UDP connection to port 6348", e); } finally { threadEnder.countDown(); } } }); t.setDaemon(true); try { t.start(); // startup connection one in original thread uconn1 = udpSelectorProvider.openSocketChannel().socket(); uconn1.connect(new InetSocketAddress("127.0.0.1", 6346), 2000); } finally { assertTrue(threadEnder.await(2000 * 60, TimeUnit.MILLISECONDS)); } } } /** * Reads an int from <code>is</code> in big-endian byte-order. */ private int readInt(InputStream is) throws IOException { int result = 0; for (int i = 0; i < 4; i++) { int read = is.read(); if (read == -1) { throw new EOFException(); } result |= read << 8 * (3 - i); } return result; } /** * Writes an int to <code>os</code> in big-endian byte-order. */ private void writeInt(int x, OutputStream os) throws IOException { os.write((byte)(x >> 24)); os.write((byte)(x >> 16)); os.write((byte)(x >> 8)); os.write((byte) x ); } }