/** No read-delimiters specified, so fire events as data segments arrive. */ @SuppressWarnings("resource") private void readUnboundedRawLoop(Socket socket) throws IOException { CountableInputStream cis = new CountableInputStream(socket.getInputStream(), _counterRecvOps, _counterRecvRate); // create a buffer that'll be reused if (_buffer == null) _buffer = new byte[1024 * 10 * 2]; while (!_shutdown) { int bytesRead = cis.read(_buffer); if (bytesRead <= 0) break; String segment = bufferToString(_buffer, 0, bytesRead); // fire the handler handleReceivedData(segment); } // the peer has gracefully closed down the connection or we're shutting down if (!_shutdown) { // then fire the disconnected callback Handler.tryHandle(_disconnectedCallback, _callbackErrorHandler); } }
/** (convenience method) */ private void sendBufferNow0(OutputStream os, byte[] buffer, String origData) { try { os.write(buffer); } catch (Exception exc) { // ignore } Handler.tryHandle(_sentCallback, origData, _callbackErrorHandler); }
/** * The reading loop will continually read until an error occurs or the stream is gracefully ended * by the peer. * * <p>("resource" warning suppression applies to 'bis'. It's not valid because socket itself gets * closed) */ @SuppressWarnings("resource") private void readTextLoop(Socket socket) throws Exception { InputStream in = socket.getInputStream(); BufferedInputStream bis = new BufferedInputStream( new CountableInputStream(in, _counterRecvOps, _counterRecvRate), 1024); // create a buffer that'll be reused // start off small, will grow as needed BufferBuilder bb = new BufferBuilder(256); while (!_shutdown) { int c = bis.read(); if (c < 0) break; if (charMatches((char) c, _receiveDelimiters)) { String str = bb.getTrimmedString(); if (str != null) handleReceivedData(str); bb.reset(); } else { if (bb.getSize() >= MAX_SEGMENT_ALLOWED) { // drop the connection throw new IOException( "Too much data arrived (at least " + bb.getSize() / 1024 + " KB) before any delimeter was present; dropping connection."); } bb.append((byte) c); } } // (while) // the peer has gracefully closed down the connection or we're shutting down if (!_shutdown) { // send out last data String str = bb.getTrimmedString(); if (str != null) handleReceivedData(str); // then fire the disconnected callback Handler.tryHandle(_disconnectedCallback, _callbackErrorHandler); } }
/** * The reading loop will continually read until an error occurs or the stream is gracefully ended * by the peer. * * <p>(Suppress 'resource' ignored because 'in' stream is closed in calling function.) */ @SuppressWarnings("resource") private void readLengthDelimitedLoop(Socket socket, char startFlag, Character optStopFlag) throws Exception { InputStream in = socket.getInputStream(); BufferedInputStream bis = new BufferedInputStream( new CountableInputStream(in, _counterRecvOps, _counterRecvRate), 1024); // create a buffer that'll be reused // start off small, will grow as needed BufferBuilder bb = new BufferBuilder(256); // create an additional temporary buffer which can be used with 'length' delimited parsing byte[] buffer = new byte[1024]; // got 'start flag'? boolean synced = false; // how many bytes to read int bytesToRead = 0; while (!_shutdown) { int c = bis.read(); if (c < 0) break; // discard data until we sync up with the 'start' flag if (!synced) { if (c == startFlag) { synced = true; // (put back) bb.append(c); // read 'length' (value excludes 'start', 'stop' flags) int len1 = Stream.readUnsignedByte(bis); int len2 = Stream.readUnsignedByte(bis); int length = Stream.readUnsignedShort(len1, len2); if (length >= MAX_SEGMENT_ALLOWED) // drop the connection throw new IOException( "Packet length indicates it is too large to accept (" + Formatting.formatByteLength(length) + ")."); // (put back) bb.append(len1); bb.append(len2); // already read 2 ('length') length bytesToRead = length - 2; } else { // not synced, so keep discarding continue; } } while (!_shutdown) { // we're synced, so can read as much as we need to if (bytesToRead > 0) { int bytesRead = bis.read(buffer, 0, Math.min(bytesToRead, buffer.length)); if (bytesRead < 0) throw new EOFException(); bytesToRead -= bytesRead; // 'copy' the buffer bb.append(buffer, 0, bytesRead); } else { // we've read enough break; } } if (optStopFlag != null) { c = Stream.readUnsignedByte(bis); if (c != optStopFlag.charValue()) throw new IOException("Stop flag was not matched after reading the packet bytes."); // (put back) bb.append(c); // fire the handled event String str = bb.getRawString(); handleReceivedData(str); bb.reset(); synced = false; } } // (while) // the peer has gracefully closed down the connection or we're shutting down if (!_shutdown) { // drop left over data and fire the disconnected callback Handler.tryHandle(_disconnectedCallback, _callbackErrorHandler); } }
/** Establishes a socket and continually reads. */ private void connectAndRead() throws Exception { Socket socket = null; OutputStream os = null; try { socket = new Socket(); InetSocketAddress socketAddress = parseAndResolveDestination(_dest); socket.connect(socketAddress, CONNECT_TIMEOUT); _counterConnections.incr(); socket.setSoTimeout(_timeout); // 'inject' countable stream os = new CountableOutputStream(socket.getOutputStream(), _counterSendOps, _counterSendRate); // update flag _lastSuccessfulConnection = System.nanoTime(); synchronized (_lock) { if (_shutdown) return; _socket = socket; _outputStream = os; // connection has been successful so reset variables // related to exponential back-off _recentlyConnected = true; _backoffTime = MIN_CONNETION_GAP; } // fire the connected event _callbackHandler.handle(_connectedCallback, _callbackErrorHandler); // start reading if (_mode == Modes.LengthDelimitedRaw) readLengthDelimitedLoop(socket, _binaryStartFlag, _binaryStopFlag); else if (_mode == Modes.CharacterDelimitedText) readTextLoop(socket); else { // mode is 'UnboundedRaw' readUnboundedRawLoop(socket); } // (any non-timeout exceptions will be propagated to caller...) } catch (Exception exc) { // fire the disconnected handler if was previously connected if (os != null) Handler.tryHandle(_disconnectedCallback, _callbackErrorHandler); throw exc; } finally { // always gracefully close the socket and invalidate the socket fields synchronized (_lock) { _socket = null; _outputStream = null; } Stream.safeClose(socket); } }