java.lang.Object
net.i2p.client.streaming.impl.ConnectionDataReceiver
All Implemented Interfaces:
MessageOutputStream.DataReceiver

class ConnectionDataReceiverextends Object implements MessageOutputStream.DataReceiver
Receive data from the MessageOutputStream, build a packet, and send it through a connection. The write calls on this do NOT block, but they also do not necessarily imply immediate delivery, or even the generation of a new packet. This class is the only one that builds useful outbound Packet objects.

MessageOutputStream -> ConnectionDataReceiver -> Connection -> PacketQueue -> I2PSession

There's one of these per MessageOutputStream. It stores no state. It sends everything to the Connection unless the Connection is closed.

  • Field Details

    • _context

      private final I2PAppContext _context
      Application context.
    • _log

      private final Log _log
      Class logger.
    • _connection

      private final Connection _connection
      Streaming connection.
    • _dummyStatus

      private static final MessageOutputStream.WriteStatus _dummyStatus
      Dummy status.
    • _payloadCache

      private static final ByteCache _payloadCache
      Payload cache.
    • EMPTY_PAYLOAD

      private static final ByteArray EMPTY_PAYLOAD
      Reusable empty payload -- avoids per-packet ByteArray allocation for ACK-only packets. Shared across all instances via _dataLock in MessageOutputStream.
  • Constructor Details

    • ConnectionDataReceiver

      public ConnectionDataReceiver(I2PAppContext ctx, Connection con)
      Create a data receiver for the given connection.
      Parameters:
      ctx - application context, used to look up this class's logger
      con - non-null
  • Method Details

    • writeInProcess

      public boolean writeInProcess()
      This tells the flusher in MessageOutputStream whether to flush. It won't flush if this returns true.

      It was: return con.getUnackedPacketsSent() > 0 (i.e. Nagle) But then, for data that fills more than one packet, the last part of the data isn't sent until all the previous packets are acked. Which is very slow. The poor interaction of Nagle and Delayed Acknowledgements is well-documented.

      So let's send data along unless the outbound window is full. (i.e. no-Nagle or TCP_NODELAY)

      Probably should have a configuration option for this.

      Specified by:
      writeInProcess in interface MessageOutputStream.DataReceiver
      Returns:
      true while the outbound window is full, so nothing should be flushed yet
    • writeData

      public MessageOutputStream.WriteStatus writeData(byte[] buf, int off, int size)
      Send some data through the connection, or if there is no new data, this may generate a packet with a plain ACK/NACK or CLOSE, or nothing whatsoever if there's nothing new to send.

      This is called from MessageOutputStream, i.e. data from the client.

      Specified by:
      writeData in interface MessageOutputStream.DataReceiver
      Parameters:
      buf - data to be sent - may be null
      off - offset into the buffer to start writing from
      size - how many bytes of the buffer to write (may be 0)
      Returns:
      an object to allow optional blocking for data acceptance or delivery.
    • send

      public PacketLocal send(byte[] buf, int off, int size)
      Send some data through the connection, attaching any appropriate flags onto the packet.

      Called externally from Connection with args (null, 0, 0) to send an ack.

      Parameters:
      buf - data to be sent - may be null
      off - offset into the buffer to start writing from
      size - how many bytes of the buffer to write (may be 0)
      Returns:
      the packet sent
    • send

      public PacketLocal send(byte[] buf, int off, int size, boolean forceIncrement)
      Called externally from Connection with args (null, 0, 0, true) to send an empty data packet.
      Parameters:
      buf - data to be sent - may be null
      off - offset into the buffer to start writing from
      size - how many bytes of the buffer to write (may be 0)
      forceIncrement - even if the buffer is empty, increment the packetId so we get an ACK back
      Returns:
      the packet sent
    • isAckOnly

      private boolean isAckOnly(int size)
      Whether the packet carries no data and only serves to elicit an ACK.
      Parameters:
      size - the payload size in bytes, 0 or less meaning no data
      Returns:
      true if there is no data, this is not a SYN, and it is not the first CLOSE packet
    • buildPacket

      private PacketLocal buildPacket(byte[] buf, int off, int size, boolean forceIncrement)
      Compose a packet. Most flags are set here; however, some are set in Connection.sendPacket(PacketLocal) and Connection.ResendPacketEvent.retransmit(). Take care not to set the same options both here and in Connection.
      Parameters:
      buf - data to be sent - may be null
      off - offset into the buffer to start writing from
      size - how many bytes of the buffer to write (may be 0)
      forceIncrement - even if the buffer is empty, increment the packetId so we get an ACK back
      Returns:
      the packet to be sent
    • destroy

      void destroy()
      Release resources held by this receiver.