Exemple #1
0
  /** Drops this socket; may trigger a reconnect. */
  public void drop() {
    synchronized (_lock) {
      if (_shutdown) return;

      Stream.safeClose(_socket);
    }
  }
Exemple #2
0
  /** Creates a new socket, cleaning up if anything goes wrong in the process */
  private MulticastSocket createMulticastSocket(String label, InetAddress intf, int port)
      throws Exception {
    MulticastSocket socket = null;

    try {
      _logger.info(
          "Preparing {} socket. interface:{}, port:{}, group:{}",
          label,
          (intf == null ? "default" : intf),
          (port == 0 ? "any" : port),
          _group);

      // in previous versions the interface was selected using constructor instead of
      // 'socket.setInterface(intf)'
      // but that uncovered side-effect in OSX which caused 'cannot assign address' Java bug

      socket = new MulticastSocket(port); // (port '0' means any port)

      if (intf != null) socket.setInterface(intf);

      // join the multicast group
      socket.joinGroup(_group);

      _logger.info("{} ready. localAddr:{}", label, socket.getLocalSocketAddress());

      return socket;

    } catch (Exception exc) {
      Stream.safeClose(socket);

      throw exc;
    }
  }
Exemple #3
0
  /** Permanently shuts down all related resources. */
  @Override
  public void close() throws IOException {
    // clear flag
    _enabled = false;

    // release timers
    for (TimerTask timer : _timers) timer.cancel();

    Stream.safeClose(_sendSocket, _receiveSocket, _hardLinksSocket);
  }
Exemple #4
0
  /**
   * Check if the main receiver has been silent for some time so recycle the socket for best
   * resilience.
   */
  private void recycleReceiverIfNecessary(long currentTime) {
    long timeDiff = (currentTime - _lastExternalMulticastPacket) / 1000000;

    if (timeDiff > SILENCE_TOLERANCE) {
      _logger.info(
          "There appears to be external silence on the multicast receiver (this may or may not be expected); the socket will be recycled to ensure resilience.");

      synchronized (_lock) {
        // recycle receiver which will in turn recycle sender
        _recycleReceiver = true;

        // "signal" the other thread
        Stream.safeClose(_receiveSocket);
      }
    }
  }
Exemple #5
0
  /** Used to monitor address changes on the the interface. */
  private void handleInterfaceCheck() {
    try {
      int port = super.getAdvertisementPort();
      if (port < 0) {
        // can't compose a nodel address yet
        _logger.info("(nodel server port still not available; will wait.)");

        return;
      }

      InetAddress localIPv4Address = getLocalIPv4Address();
      String nodelAddress = "tcp://" + localIPv4Address.getHostAddress() + ":" + port;

      if (nodelAddress.equals(_nodelAddress))
        // nothing to do
        return;

      // the address has changed so should update advertisements
      _logger.info(
          "An address change has been detected. previous={}, current={}",
          _nodelAddress,
          nodelAddress);

      _nodelAddress = "tcp://" + localIPv4Address.getHostAddress() + ":" + port;
      _httpAddress =
          "http://"
              + localIPv4Address.getHostAddress()
              + ":"
              + Nodel.getHTTPPort()
              + Nodel.getHTTPSuffix();

      Nodel.updateHTTPAddress(_httpAddress);

      synchronized (_lock) {
        // recycle receiver which will in turn recycle sender
        _recycleReceiver = true;

        // "signal" thread
        Stream.safeClose(_receiveSocket);
      }

    } catch (Exception exc) {
      _logger.warn("'handleInterfaceCheck' did not complete cleanly; ignoring for now.", exc);
    }
  } // (method)
Exemple #6
0
  /** Permanently shuts down this managed TCP connection. */
  @Override
  public void close() {
    synchronized (_lock) {
      if (_shutdown) return;

      _shutdown = true;

      _outputStream = null;

      if (_startTimer != null) _startTimer.cancel();

      Stream.safeClose(_socket);

      _socket = null;

      // notify the connection and receive thread if it happens to be waiting
      _lock.notify();
    }
  }
Exemple #7
0
  /** (thread entry-point) */
  private void hardLinksReceiverThreadMain() {
    _logger.info("Instructed to use hardlinks. address:{}", _hardLinksAddresses);

    DatagramSocket socket = null;
    try {
      // initialise a UDP socket on an arbitrary port
      _hardLinksSocket = new DatagramSocket();

      socket = _hardLinksSocket;

      while (_enabled) {
        DatagramPacket dp = UDPPacketRecycleQueue.instance().getReadyToUsePacket();

        // ('returnPacket' will be called in 'catch' or later after use in thread-pool)

        try {
          socket.receive(dp);

          s_unicastInData.addAndGet(dp.getLength());
          s_unicastInOps.incrementAndGet();

          enqueueForProcessing(dp, s_hardLinksSocketlabel);

        } catch (Exception exc) {
          UDPPacketRecycleQueue.instance().returnPacket(dp);

          // ignore
        }
      } // (while)

    } catch (Exception exc) {
      _logger.warn("Failed to initialise [" + s_hardLinksSocketlabel + "] socket.", exc);
    } finally {
      _logger.info("[" + s_hardLinksSocketlabel + "] thread run to completion.");

      // close for good measure
      Stream.safeClose(socket);
    }
  }
Exemple #8
0
  /**
   * 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);
    }
  }
Exemple #9
0
  /** 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);
    }
  }
Exemple #10
0
  /** (thread entry-point) */
  private void unicastReceiverThreadMain() {
    while (_enabled) {
      MulticastSocket socket = null;

      try {
        synchronized (_lock) {
          // clear flag regardless
          _recycleSender = false;
        }

        socket = createMulticastSocket(s_sendSocketLabel, s_interface, 0);

        // make sure a recycle request hasn't since occurred
        synchronized (_lock) {
          if (_recycleSender) {
            Stream.safeClose(socket);
            continue;
          }

          _sendSocket = socket;
        }

        while (_enabled) {
          DatagramPacket dp = UDPPacketRecycleQueue.instance().getReadyToUsePacket();

          try {
            socket.receive(dp);

          } catch (Exception exc) {
            UDPPacketRecycleQueue.instance().returnPacket(dp);

            throw exc;
          }

          if (dp.getAddress().isMulticastAddress()) {
            s_multicastInData.addAndGet(dp.getLength());
            s_multicastInOps.incrementAndGet();
          } else {
            s_unicastInData.addAndGet(dp.getLength());
            s_unicastInOps.incrementAndGet();
          }

          enqueueForProcessing(dp, s_sendSocketLabel);
        } // (inner while)

      } catch (Exception exc) {
        boolean wasClosed = (socket != null && socket.isClosed());

        // clean up regardless
        Stream.safeClose(socket);

        synchronized (_lock) {
          if (!_enabled) break;

          if (wasClosed)
            _logger.info(
                s_sendSocketLabel + " was signalled to gracefully close. Will reinitialise...");
          else _logger.warn(s_sendSocketLabel + " receive failed; will reinitialise...", exc);

          // stagger retry
          Threads.waitOnSync(_lock, 333);
        }
      }
    } // (outer while)

    _logger.info("This thread has run to completion.");
  }
Exemple #11
0
  /** (thread entry-point) */
  private void multicastReceiverThreadMain() {
    while (_enabled) {
      MulticastSocket socket = null;

      try {
        synchronized (_lock) {
          // clear flag regardless
          _recycleReceiver = false;
        }

        socket = createMulticastSocket(s_receiveSocketlabel, s_interface, MDNS_PORT);

        synchronized (_lock) {
          // make sure not flagged since reset
          if (_recycleReceiver) {
            Stream.safeClose(socket);
            continue;
          }

          _receiveSocket = socket;
        }

        while (_enabled) {
          DatagramPacket dp = UDPPacketRecycleQueue.instance().getReadyToUsePacket();

          // ('returnPacket' will be called in 'catch' or later after use in thread-pool)

          try {
            socket.receive(dp);

          } catch (Exception exc) {
            UDPPacketRecycleQueue.instance().returnPacket(dp);

            throw exc;
          }

          InetAddress recvAddr = dp.getAddress();

          if (recvAddr.isMulticastAddress()) {
            s_multicastInData.addAndGet(dp.getLength());
            s_multicastInOps.incrementAndGet();
          } else {
            s_unicastInData.addAndGet(dp.getLength());
            s_unicastInOps.incrementAndGet();
          }

          // check whether it's external i.e. completely different IP address
          // (local multicasting would almost always be reliable)

          MulticastSocket otherLocal = _sendSocket;
          boolean isLocal = (otherLocal != null && recvAddr.equals(otherLocal.getLocalAddress()));

          // update counter which is used to detect silence
          if (!isLocal) _lastExternalMulticastPacket = System.nanoTime();

          enqueueForProcessing(dp, s_receiveSocketlabel);
        } // (inner while)

      } catch (Exception exc) {
        // (timeouts and general IO problems)

        // clean up regardless
        Stream.safeClose(socket);

        synchronized (_lock) {
          if (!_enabled) break;

          if (_recycleReceiver)
            _logger.info(s_receiveSocketlabel + " was gracefully closed. Will reinitialise...");
          else
            _logger.warn(
                s_receiveSocketlabel
                    + " receive failed; this may be a transitional condition. Will reinitialise... message was '"
                    + exc.toString()
                    + "'");

          // set flag
          _recycleSender = true;
          // "signal" other thread
          Stream.safeClose(_sendSocket);

          // stagger retry
          Threads.waitOnSync(_lock, 333);
        }
      }
    } // (outer while)

    _logger.info("This thread has run to completion.");
  } // (method)