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;
}
Aggregations