feat: expand Transport
This commit is contained in:
1 parent
f8076ad159
commit
1f7895d714
1 file changed
+159
@@ -323,3 +323,162 @@ bool UnreliableChannel::accept(const Datagram::Fragment& piece) {
|
|||||||
anyDelivered = true;
|
anyDelivered = true;
|
||||||
incoming.erase(it);
|
incoming.erase(it);
|
||||||
|
|
||||||
|
// Anything still half-assembled below the newest complete message can only
|
||||||
|
// ever produce a stale update, so it is abandoned right away.
|
||||||
|
for (auto stalePart = incoming.begin(); stalePart != incoming.end();) {
|
||||||
|
if (stalePart->first < highestDelivered) {
|
||||||
|
++stale;
|
||||||
|
stalePart = incoming.erase(stalePart);
|
||||||
|
} else {
|
||||||
|
++stalePart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnreliableChannel::popDelivered(ByteBuffer& out) {
|
||||||
|
if (delivered.empty()) return false;
|
||||||
|
out = std::move(delivered.front());
|
||||||
|
delivered.pop_front();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transport::Transport(LoopbackLink& link, int endpoint, TransportConfig config)
|
||||||
|
: link(&link), endpoint(endpoint), config(config) {
|
||||||
|
std::size_t floorMtu = Datagram::headerSize() + Datagram::maxFragmentOverhead() + 16;
|
||||||
|
if (this->config.mtu < floorMtu) this->config.mtu = floorMtu;
|
||||||
|
if (this->config.sendWindow == 0) this->config.sendWindow = 1;
|
||||||
|
if (this->config.retransmitTimeoutTicks == 0) this->config.retransmitTimeoutTicks = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Transport::maxFragmentPayload() const {
|
||||||
|
return config.mtu - Datagram::headerSize() - Datagram::maxFragmentOverhead();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::sendReliable(const uint8_t* payload, std::size_t size) {
|
||||||
|
reliable.queueMessage(payload, size, maxFragmentPayload());
|
||||||
|
++statistics.messagesQueued;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::sendReliable(const ByteBuffer& payload) {
|
||||||
|
sendReliable(payload.data(), payload.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::sendUnreliable(const uint8_t* payload, std::size_t size) {
|
||||||
|
unreliable.queueMessage(payload, size, maxFragmentPayload());
|
||||||
|
++statistics.messagesQueued;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::sendUnreliable(const ByteBuffer& payload) {
|
||||||
|
sendUnreliable(payload.data(), payload.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transport::registerIncoming(SequenceNumber sequence) {
|
||||||
|
if (!anyReceived) {
|
||||||
|
anyReceived = true;
|
||||||
|
remoteLatest = sequence;
|
||||||
|
remoteHistory = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (sequence == remoteLatest) return false;
|
||||||
|
|
||||||
|
if (sequenceGreater(sequence, remoteLatest)) {
|
||||||
|
uint16_t shift = sequenceDistance(sequence, remoteLatest);
|
||||||
|
if (shift >= 32) {
|
||||||
|
remoteHistory = 0;
|
||||||
|
} else {
|
||||||
|
remoteHistory = (remoteHistory << shift) | (1u << (shift - 1));
|
||||||
|
}
|
||||||
|
remoteLatest = sequence;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t behind = sequenceDistance(remoteLatest, sequence);
|
||||||
|
if (behind > 32) return false;
|
||||||
|
uint32_t bit = 1u << (behind - 1);
|
||||||
|
if ((remoteHistory & bit) != 0) return false;
|
||||||
|
remoteHistory |= bit;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::acknowledge(SequenceNumber sequence, uint64_t tick) {
|
||||||
|
for (std::size_t i = 0; i < inFlight.size(); ++i) {
|
||||||
|
if (inFlight[i].sequence != sequence) continue;
|
||||||
|
sampleRtt(tick - inFlight[i].sentTick);
|
||||||
|
for (uint64_t key : inFlight[i].reliableKeys) reliable.markAcked(key);
|
||||||
|
inFlight.erase(inFlight.begin() + static_cast<std::ptrdiff_t>(i));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::applyAcks(SequenceNumber ackLatest, AckBitfield ackHistory, uint64_t tick) {
|
||||||
|
acknowledge(ackLatest, tick);
|
||||||
|
for (int bit = 0; bit < 32; ++bit) {
|
||||||
|
if ((ackHistory & (1u << bit)) == 0) continue;
|
||||||
|
acknowledge(static_cast<SequenceNumber>(ackLatest - 1 - bit), tick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::sampleRtt(uint64_t sample) {
|
||||||
|
double value = static_cast<double>(sample);
|
||||||
|
if (statistics.smoothedRttTicks <= 0.0) {
|
||||||
|
statistics.smoothedRttTicks = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double weight = static_cast<double>(config.rttSmoothingPercent) / 100.0;
|
||||||
|
statistics.smoothedRttTicks += (value - statistics.smoothedRttTicks) * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transport::pump(uint64_t tick) {
|
||||||
|
link->receive(endpoint, tick, arrivals);
|
||||||
|
for (const ByteBuffer& raw : arrivals) {
|
||||||
|
++statistics.datagramsReceived;
|
||||||
|
statistics.bytesReceived += raw.size();
|
||||||
|
|
||||||
|
Datagram datagram;
|
||||||
|
if (!datagram.decode(raw.data(), raw.size())) continue;
|
||||||
|
if (!registerIncoming(datagram.sequence)) {
|
||||||
|
++statistics.duplicatesDropped;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (datagram.ackValid) applyAcks(datagram.ackLatest, datagram.ackHistory, tick);
|
||||||
|
if (!datagram.fragments.empty()) ackPending = true;
|
||||||
|
|
||||||
|
for (const Datagram::Fragment& piece : datagram.fragments) {
|
||||||
|
bool accepted = piece.reliable ? reliable.accept(piece) : unreliable.accept(piece);
|
||||||
|
if (!accepted) ++statistics.staleDropped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transport::buildDatagram(Datagram& out) {
|
||||||
|
out.fragments.clear();
|
||||||
|
std::size_t used = Datagram::headerSize();
|
||||||
|
|
||||||
|
std::vector<uint64_t> ready;
|
||||||
|
reliable.collectReady(ready);
|
||||||
|
for (uint64_t key : ready) {
|
||||||
|
const ReliableChannel::OutgoingFragment* piece = reliable.fragment(key);
|
||||||
|
std::size_t cost = fragmentEncodedSize(piece->messageId, piece->fragmentIndex,
|
||||||
|
piece->fragmentCount, piece->bytes.size());
|
||||||
|
if (used + cost > config.mtu) break;
|
||||||
|
Datagram::Fragment wire;
|
||||||
|
wire.reliable = true;
|
||||||
|
wire.messageId = piece->messageId;
|
||||||
|
wire.fragmentIndex = piece->fragmentIndex;
|
||||||
|
wire.fragmentCount = piece->fragmentCount;
|
||||||
|
wire.bytes = piece->bytes;
|
||||||
|
out.fragments.push_back(std::move(wire));
|
||||||
|
used += cost;
|
||||||
|
reliable.markInFlight(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (const Datagram::Fragment* piece = unreliable.peekOutgoing()) {
|
||||||
|
std::size_t cost = fragmentEncodedSize(piece->messageId, piece->fragmentIndex,
|
||||||
|
piece->fragmentCount, piece->bytes.size());
|
||||||
|
if (used + cost > config.mtu) break;
|
||||||
|
out.fragments.push_back(*piece);
|
||||||
|
used += cost;
|
||||||
|
unreliable.dropOutgoing();
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user