Search in sources :

Example 1 with Fragment

use of net.i2p.router.transport.udp.PacketBuilder.Fragment in project i2p.i2p by i2p.

the class OutboundMessageFragments method preparePackets.

/**
 *  @return null if state or peer is null
 */
private List<UDPPacket> preparePackets(List<OutboundMessageState> states, PeerState peer) {
    if (states == null || peer == null)
        return null;
    // ok, simplest possible thing is to always tack on the bitfields if
    List<Long> msgIds = peer.getCurrentFullACKs();
    int newFullAckCount = msgIds.size();
    msgIds.addAll(peer.getCurrentResendACKs());
    List<ACKBitfield> partialACKBitfields = new ArrayList<ACKBitfield>();
    peer.fetchPartialACKs(partialACKBitfields);
    int piggybackedPartialACK = partialACKBitfields.size();
    // getCurrentFullACKs() already makes a copy, do we need to copy again?
    // YES because buildPacket() now removes them (maybe)
    List<Long> remaining = new ArrayList<Long>(msgIds);
    // build the list of fragments to send
    List<Fragment> toSend = new ArrayList<Fragment>(8);
    for (OutboundMessageState state : states) {
        int fragments = state.getFragmentCount();
        int queued = 0;
        for (int i = 0; i < fragments; i++) {
            if (state.needsSending(i)) {
                toSend.add(new Fragment(state, i));
                queued++;
            }
        }
        // per-state stats
        if (queued > 0 && state.getPushCount() > 1) {
            peer.messageRetransmitted(queued);
            // _packetsRetransmitted += toSend; // lifetime for the transport
            _context.statManager().addRateData("udp.peerPacketsRetransmitted", peer.getPacketsRetransmitted(), peer.getPacketsTransmitted());
            _context.statManager().addRateData("udp.packetsRetransmitted", state.getLifetime(), peer.getPacketsTransmitted());
            if (_log.shouldLog(Log.INFO))
                _log.info("Retransmitting " + state + " to " + peer);
            _context.statManager().addRateData("udp.sendVolleyTime", state.getLifetime(), queued);
        }
    }
    if (toSend.isEmpty())
        return null;
    int fragmentsToSend = toSend.size();
    // sort by size, biggest first
    // don't bother unless more than one state (fragments are already sorted within a state)
    // This puts the DeliveryStatusMessage after the DatabaseStoreMessage, don't do it for now.
    // It also undoes the ordering of the priority queue in PeerState.
    // if (fragmentsToSend > 1 && states.size() > 1)
    // Collections.sort(toSend, new FragmentComparator());
    List<Fragment> sendNext = new ArrayList<Fragment>(Math.min(toSend.size(), 4));
    List<UDPPacket> rv = new ArrayList<UDPPacket>(toSend.size());
    for (int i = 0; i < toSend.size(); i++) {
        Fragment next = toSend.get(i);
        sendNext.add(next);
        OutboundMessageState state = next.state;
        OutNetMessage msg = state.getMessage();
        int msgType = (msg != null) ? msg.getMessageTypeId() : -1;
        if (_log.shouldLog(Log.INFO))
            _log.info("Building packet for " + next + " to " + peer);
        int curTotalDataSize = state.fragmentSize(next.num);
        // now stuff in more fragments if they fit
        if (i + 1 < toSend.size()) {
            int maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
            for (int j = i + 1; j < toSend.size(); j++) {
                next = toSend.get(j);
                int nextDataSize = next.state.fragmentSize(next.num);
                // if (_builder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) {
                if (nextDataSize <= maxAvail) {
                    // add it
                    toSend.remove(j);
                    j--;
                    sendNext.add(next);
                    curTotalDataSize += nextDataSize;
                    maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
                    if (_log.shouldLog(Log.INFO))
                        _log.info("Adding in additional " + next + " to " + peer);
                }
            // else too big
            }
        }
        int before = remaining.size();
        UDPPacket pkt = _builder.buildPacket(sendNext, peer, remaining, newFullAckCount, partialACKBitfields);
        if (pkt != null) {
            if (_log.shouldLog(Log.INFO))
                _log.info("Built packet with " + sendNext.size() + " fragments totalling " + curTotalDataSize + " data bytes to " + peer);
            _context.statManager().addRateData("udp.sendFragmentsPerPacket", sendNext.size());
        }
        sendNext.clear();
        if (pkt == null) {
            if (_log.shouldLog(Log.WARN))
                _log.info("Build packet FAIL for " + DataHelper.toString(sendNext) + " to " + peer);
            continue;
        }
        rv.add(pkt);
        int after = remaining.size();
        newFullAckCount = Math.max(0, newFullAckCount - (before - after));
        int piggybackedAck = 0;
        if (msgIds.size() != remaining.size()) {
            for (int j = 0; j < msgIds.size(); j++) {
                Long id = msgIds.get(j);
                if (!remaining.contains(id)) {
                    peer.removeACKMessage(id);
                    piggybackedAck++;
                }
            }
        }
        if (piggybackedAck > 0)
            _context.statManager().addRateData("udp.sendPiggyback", piggybackedAck);
        if (piggybackedPartialACK - partialACKBitfields.size() > 0)
            _context.statManager().addRateData("udp.sendPiggybackPartial", piggybackedPartialACK - partialACKBitfields.size(), state.getLifetime());
        // following for debugging and stats
        pkt.setFragmentCount(sendNext.size());
        // type of first fragment
        pkt.setMessageType(msgType);
    }
    int sent = rv.size();
    peer.packetsTransmitted(sent);
    if (_log.shouldLog(Log.INFO))
        _log.info("Sent " + fragmentsToSend + " fragments of " + states.size() + " messages in " + sent + " packets to " + peer);
    return rv;
}
Also used : OutNetMessage(net.i2p.router.OutNetMessage) ArrayList(java.util.ArrayList) Fragment(net.i2p.router.transport.udp.PacketBuilder.Fragment)

Aggregations

ArrayList (java.util.ArrayList)1 OutNetMessage (net.i2p.router.OutNetMessage)1 Fragment (net.i2p.router.transport.udp.PacketBuilder.Fragment)1