/** Initiate a new multiplexed connection through the underlying connection. */ public synchronized TCPConnection openConnection() throws IOException { // generate ID that should not be already used // If all possible 32768 IDs are used, // this method will block searching for a new ID forever. int id; do { lastID = (++lastID) & 0x7FFF; id = lastID; // The orig flag (copied to the high bit of the ID) is used // to have two distinct ranges to choose IDs from for the // two endpoints. if (orig) id |= 0x8000; } while (connectionTable.get(id) != null); // create multiplexing streams and bookkeeping information MultiplexConnectionInfo info = new MultiplexConnectionInfo(id); info.in = new MultiplexInputStream(this, info, 2048); info.out = new MultiplexOutputStream(this, info, 2048); // add to connection table if multiplexer has not died synchronized (connectionTable) { if (!alive) throw new IOException("Multiplexer connection dead"); if (numConnections >= maxConnections) throw new IOException( "Cannot exceed " + maxConnections + " simultaneous multiplexed connections"); connectionTable.put(id, info); ++numConnections; } // inform remote endpoint of new connection synchronized (dataOut) { try { dataOut.writeByte(OPEN); dataOut.writeShort(id); dataOut.flush(); } catch (IOException e) { multiplexLog.log(Log.BRIEF, "exception: ", e); shutDown(); throw e; } } return new TCPConnection(channel, info.in, info.out); }
/** Process multiplexing protocol received from underlying connection. */ public void run() throws IOException { try { int op, id, length; MultiplexConnectionInfo info; while (true) { // read next op code from remote endpoint op = dataIn.readUnsignedByte(); switch (op) { // remote endpoint initiating new connection case OPEN: id = dataIn.readUnsignedShort(); if (multiplexLog.isLoggable(Log.VERBOSE)) { multiplexLog.log(Log.VERBOSE, "operation OPEN " + id); } info = connectionTable.get(id); if (info != null) throw new IOException("OPEN: Connection ID already exists"); info = new MultiplexConnectionInfo(id); info.in = new MultiplexInputStream(this, info, 2048); info.out = new MultiplexOutputStream(this, info, 2048); synchronized (connectionTable) { connectionTable.put(id, info); ++numConnections; } sun.rmi.transport.Connection conn; conn = new TCPConnection(channel, info.in, info.out); channel.acceptMultiplexConnection(conn); break; // remote endpoint closing connection case CLOSE: id = dataIn.readUnsignedShort(); if (multiplexLog.isLoggable(Log.VERBOSE)) { multiplexLog.log(Log.VERBOSE, "operation CLOSE " + id); } info = connectionTable.get(id); if (info == null) throw new IOException("CLOSE: Invalid connection ID"); info.in.disconnect(); info.out.disconnect(); if (!info.closed) sendCloseAck(info); synchronized (connectionTable) { connectionTable.remove(id); --numConnections; } break; // remote endpoint acknowledging close of connection case CLOSEACK: id = dataIn.readUnsignedShort(); if (multiplexLog.isLoggable(Log.VERBOSE)) { multiplexLog.log(Log.VERBOSE, "operation CLOSEACK " + id); } info = connectionTable.get(id); if (info == null) throw new IOException("CLOSEACK: Invalid connection ID"); if (!info.closed) throw new IOException("CLOSEACK: Connection not closed"); info.in.disconnect(); info.out.disconnect(); synchronized (connectionTable) { connectionTable.remove(id); --numConnections; } break; // remote endpoint declaring additional bytes receivable case REQUEST: id = dataIn.readUnsignedShort(); info = connectionTable.get(id); if (info == null) throw new IOException("REQUEST: Invalid connection ID"); length = dataIn.readInt(); if (multiplexLog.isLoggable(Log.VERBOSE)) { multiplexLog.log(Log.VERBOSE, "operation REQUEST " + id + ": " + length); } info.out.request(length); break; // remote endpoint transmitting data packet case TRANSMIT: id = dataIn.readUnsignedShort(); info = connectionTable.get(id); if (info == null) throw new IOException("SEND: Invalid connection ID"); length = dataIn.readInt(); if (multiplexLog.isLoggable(Log.VERBOSE)) { multiplexLog.log(Log.VERBOSE, "operation TRANSMIT " + id + ": " + length); } info.in.receive(length, dataIn); break; default: throw new IOException("Invalid operation: " + Integer.toHexString(op)); } } } finally { shutDown(); } }