14constexpr uint8_t kMagic0 =
'E';
15constexpr uint8_t kMagic1 =
'V';
16constexpr uint8_t kVersion = 1;
17constexpr size_t kHeaderLen = 18;
18constexpr size_t kMaxOutOfOrder = 2048;
19constexpr size_t kMaxFragments = 64;
20constexpr size_t kMaxFragCount = 2048;
21constexpr int64_t kFragExpireMs = 5000;
22constexpr int64_t kPingIntervalMs = 500;
23constexpr uint8_t kFragFlag = 1;
25enum PktType : uint8_t {
35 return std::chrono::duration_cast<std::chrono::milliseconds>(
36 std::chrono::steady_clock::now().time_since_epoch())
40void putU8(std::vector<char>&
v, uint8_t
x) {
41 v.push_back(
static_cast<char>(
x));
44void putU16(std::vector<char>&
v, uint16_t
x) {
45 v.push_back(
static_cast<char>(
x & 0xff));
46 v.push_back(
static_cast<char>((
x >> 8) & 0xff));
49void putU32(std::vector<char>&
v, uint32_t
x) {
50 for (
int i = 0; i < 4; ++i) {
51 v.push_back(
static_cast<char>((
x >> (8 * i)) & 0xff));
56 const char*
p =
nullptr;
61 bool u8(uint8_t& out) {
66 out =
static_cast<uint8_t
>(
p[
pos++]);
70 bool u16(uint16_t& out) {
71 if (!ok || n - pos < 2) {
75 out =
static_cast<uint16_t
>(uint8_t(p[pos])) |
76 (
static_cast<uint16_t
>(uint8_t(p[pos + 1])) << 8);
81 bool u32(uint32_t& out) {
82 if (!ok || n - pos < 4) {
86 out =
static_cast<uint32_t
>(uint8_t(p[pos])) |
87 (
static_cast<uint32_t
>(uint8_t(p[pos + 1])) << 8) |
88 (
static_cast<uint32_t
>(uint8_t(p[pos + 2])) << 16) |
89 (
static_cast<uint32_t
>(uint8_t(p[pos + 3])) << 24);
94 const char* rest(
size_t& len)
const {
100bool splitAddress(
const std::string& addr, std::string& host, uint16_t& port) {
101 if (addr.empty())
return false;
102 if (addr.front() ==
'[') {
103 auto close = addr.find(
']');
104 if (close == std::string::npos || close + 2 >= addr.size() || addr[close + 1] !=
':')
106 host = addr.substr(1, close - 1);
107 port =
static_cast<uint16_t
>(std::atoi(addr.c_str() + close + 2));
110 auto colon = addr.rfind(
':');
111 if (colon == std::string::npos || colon == 0)
return false;
112 host = addr.substr(0, colon);
113 port =
static_cast<uint16_t
>(std::atoi(addr.c_str() + colon + 1));
118inline bool after(uint32_t
a, uint32_t
b) {
119 return a !=
b && (
b -
a) < 0x80000000u;
130 unrelOrdBuf_.clear();
134 if (host.empty() || port == 0)
return false;
135 remoteHost_ = std::move(host);
137 remote_ = remoteHost_ +
":" + std::to_string(remotePort_);
145 if (!splitAddress(addr, host, port))
return false;
150 lossRate_ = std::max(0.f, std::min(1.f, rate));
153void UdpLink::sendDatagram(std::vector<char> pkt) {
154 if (!sock_ || !remoteSet_)
return;
155 lastSendMs_ = nowMs();
156 if (lossRate_ > 0.f &&
157 (rng_() % 10000) <
static_cast<uint32_t
>(lossRate_ * 10000.f)) {
161 sock_->
sendTo(&
d, remoteHost_, remotePort_);
165 if (!remoteSet_ || !sock_)
return;
166 if (
data ==
nullptr &&
n > 0)
return;
168 const char*
p =
static_cast<const char*
>(
data);
182 const auto build = [&](uint8_t flags,
const uint8_t*
frag,
size_t fragLen) {
183 std::vector<char> pkt;
186 putU8(pkt, kVersion);
187 putU8(pkt, wireType);
191 putU32(pkt, ackSend_);
192 putU32(pkt, ackBitsSend_);
193 pkt.insert(pkt.end(),
reinterpret_cast<const char*
>(
frag),
194 reinterpret_cast<const char*
>(
frag) + fragLen);
199 const uint32_t msgId = nextFragId_++;
200 const uint16_t fragCount =
202 for (uint16_t i = 0; i < fragCount; ++i) {
203 const size_t off =
static_cast<size_t>(i) *
kPayloadMTU;
205 std::vector<char>
frag;
207 putU16(
frag, fragCount);
209 frag.insert(
frag.end(),
p + off,
p + off + len);
210 auto pkt = build(kFragFlag,
211 reinterpret_cast<const uint8_t*
>(
frag.data()),
frag.size());
213 SendEntry& e = sendQueue_[seq];
214 if (e.pkts.empty()) {
215 e.deadlineMs = nowMs() + retryBaseMs_;
218 e.pkts.push_back(pkt);
220 sendDatagram(std::move(pkt));
225 auto pkt = build(0,
reinterpret_cast<const uint8_t*
>(
p),
n);
227 SendEntry& e = sendQueue_[seq];
229 e.pkts.push_back(pkt);
230 e.deadlineMs = nowMs() + retryBaseMs_;
233 sendDatagram(std::move(pkt));
237 if (
s.empty())
return false;
242void UdpLink::sendAckNow() {
243 std::vector<char> pkt;
246 putU8(pkt, kVersion);
251 putU32(pkt, ackSend_);
252 putU32(pkt, ackBitsSend_);
253 sendDatagram(std::move(pkt));
256void UdpLink::noteReceived() {
257 lastRecvMs_ = nowMs();
258 if (!alive_ && remoteSet_) {
260 disconnectNotified_ =
false;
264void UdpLink::pruneAcked(uint32_t ack, uint32_t bits) {
265 if (sendQueue_.empty())
return;
266 auto it = sendQueue_.begin();
267 while (it != sendQueue_.end() && !after(ack, it->first)) {
268 it = sendQueue_.erase(it);
270 for (uint32_t i = 0; i < 32; ++i) {
271 if (bits & (1u << i)) sendQueue_.erase(ack + 1 + i);
275void UdpLink::deliver(MsgType
type, uint8_t channel, std::vector<char> payload) {
277 onMessage_(
type, channel, payload.data(), payload.size());
281void UdpLink::handleData(uint8_t
type, uint8_t channel, uint32_t seq,
282 const std::vector<char>& payload) {
283 if (
type == T_UNRELIABLE) {
287 if (
type == T_ORDERED) {
288 if (seq == expectedUnrelOrd_) {
292 auto it = unrelOrdBuf_.find(expectedUnrelOrd_);
293 if (it == unrelOrdBuf_.end())
break;
295 unrelOrdBuf_.erase(it);
298 }
else if (seq > expectedUnrelOrd_ && unrelOrdBuf_.size() < kMaxOutOfOrder) {
299 unrelOrdBuf_[seq] = payload;
305 if (seq == expectedReliable_) {
309 auto it = outOfOrder_.find(expectedReliable_);
310 if (it == outOfOrder_.end())
break;
312 outOfOrder_.erase(it);
315 }
else if (seq > expectedReliable_ && outOfOrder_.size() < kMaxOutOfOrder) {
316 outOfOrder_[seq] = payload;
319 ackSend_ = expectedReliable_ - 1;
321 uint32_t scanned = 0;
322 for (
const auto& kv : outOfOrder_) {
323 if (scanned >= 32)
break;
324 if (after(ackSend_, kv.first)) {
325 const uint32_t
idx = kv.first - ackSend_ - 1;
326 if (
idx < 32) ackBitsSend_ |= (1u <<
idx);
335 if (bytes.size() < kHeaderLen)
return;
336 const char*
p = bytes.data();
337 if (
static_cast<uint8_t
>(
p[0]) != kMagic0 ||
static_cast<uint8_t
>(
p[1]) != kMagic1 ||
338 static_cast<uint8_t
>(
p[2]) != kVersion) {
341 const uint8_t
type =
static_cast<uint8_t
>(
p[3]);
342 const uint8_t channel =
static_cast<uint8_t
>(
p[4]);
343 const uint8_t flags =
static_cast<uint8_t
>(
p[5]);
345 PktView
v{
p, bytes.size(), 6,
true};
346 uint32_t seq = 0, ack = 0, ackBits = 0;
347 if (!
v.u32(seq) || !
v.u32(ack) || !
v.u32(ackBits))
return;
349 pruneAcked(ack, ackBits);
351 if (
type == T_ACK ||
type == T_PONG)
return;
352 if (
type == T_PING) {
353 std::vector<char> pong;
354 putU8(pong, kMagic0);
355 putU8(pong, kMagic1);
356 putU8(pong, kVersion);
361 putU32(pong, ackSend_);
362 putU32(pong, ackBitsSend_);
363 sendDatagram(std::move(pong));
368 const char* rest =
v.rest(restLen);
369 std::vector<char> payload;
370 if (flags & kFragFlag) {
371 PktView fv{rest, restLen, 0,
true};
373 uint16_t fragCount = 0, fragIndex = 0;
374 if (!fv.u32(msgId) || !fv.u16(fragCount) || !fv.u16(fragIndex))
return;
376 const char*
frag = fv.rest(fragLen);
377 if (fragCount == 1) {
378 payload.assign(
frag,
frag + fragLen);
380 if (fragCount > kMaxFragCount || fragIndex >= fragCount)
return;
381 auto it = fragments_.find(msgId);
382 if (it == fragments_.end()) {
383 if (fragments_.size() >= kMaxFragments)
return;
385 fb.total = fragCount;
386 fb.data.resize(
static_cast<size_t>(fragCount) *
kPayloadMTU);
387 fb.seen.resize(fragCount, 0);
389 fragments_[msgId] = std::move(fb);
390 it = fragments_.find(msgId);
392 FragBuf& fb = it->second;
393 if (fb.total != fragCount)
return;
394 if (fb.seen[fragIndex] != 0)
return;
395 fb.seen[fragIndex] = 1;
396 std::memcpy(fb.data.data() +
static_cast<size_t>(fragIndex) *
kPayloadMTU,
398 if (fragIndex == fragCount - 1) fb.lastLen = fragLen;
401 if (fb.received == fb.total) {
402 fb.data.resize((
static_cast<size_t>(fb.total) - 1) *
kPayloadMTU +
404 payload = std::move(fb.data);
405 fragments_.erase(it);
411 payload.assign(rest, rest + restLen);
414 handleData(
type, channel, seq, payload);
418 for (
auto it = sendQueue_.begin(); it != sendQueue_.end();) {
419 SendEntry& e = it->second;
420 if (e.deadlineMs <= now) {
421 if (e.attempts >= maxAttempts_) {
422 it = sendQueue_.erase(it);
423 if (!disconnectNotified_) {
424 disconnectNotified_ =
true;
426 if (onDisconnect_) onDisconnect_(
"timeout");
430 for (
const auto& pkt : e.pkts) sendDatagram(pkt);
432 e.deadlineMs = now + retryBaseMs_ * (1 << std::min(e.attempts, 6));
437 for (
auto it = fragments_.begin(); it != fragments_.end();) {
438 if (now - it->second.lastMs > kFragExpireMs) {
439 it = fragments_.erase(it);
445 if (remoteSet_ && now - lastSendMs_ >= kPingIntervalMs) {
446 std::vector<char> ping;
447 putU8(ping, kMagic0);
448 putU8(ping, kMagic1);
449 putU8(ping, kVersion);
454 putU32(ping, ackSend_);
455 putU32(ping, ackBitsSend_);
456 sendDatagram(std::move(ping));
459 if (lastRecvMs_ > 0 && now - lastRecvMs_ > timeoutMs_ && !disconnectNotified_) {
460 disconnectNotified_ =
true;
462 if (onDisconnect_) onDisconnect_(
"timeout");
In-memory byte buffer implementing eve::Data (ref-counted).
Network module: TCP/UDP/HTTP factories, background worker, and completion event plumbing....
UdpLink(Network *net, UdpSocket *sock)
void send(MsgType type, uint8_t channel, const void *data, size_t n)
bool setRemoteString(const std::string &addr)
static constexpr size_t kPayloadMTU
bool setRemote(std::string host, uint16_t port)
bool sendString(MsgType type, uint8_t channel, const std::string &s)
void setLossRate(float rate)
void onDatagram(const std::vector< char > &bytes, const std::string &from)
static constexpr size_t kMaxMessage
UDP socket backed by Poco::Net; supports connect/bind and datagram send.
bool sendTo(eve::data::ByteData *data, std::string host, uint16_t port)
Sends a datagram to an explicit host:port.