[client] network: add datagram send path

This commit is contained in:
Geoffrey McRae
2026-09-30 16:55:19 +10:00
parent c8196b9f99
commit d477d169d4
2 changed files with 129 additions and 0 deletions

View File

@@ -91,6 +91,7 @@ struct NetworkConnection
LG_TransportSession session;
uint64_t sessionEpoch;
uint64_t sendSequence;
uint64_t datagramSendSequence;
uint64_t receiveSequence;
bool helloAcknowledged;
bool authenticationComplete;
@@ -100,8 +101,11 @@ struct NetworkConnection
uint32_t receivedCapabilities;
uint16_t receivedServices;
uint16_t reliableServices;
uint16_t datagramServices;
uint32_t maxPayloadLength;
uint32_t maxDatagramPayload;
uint16_t maxDatagramSendLength;
bool datagramSendEnabled;
uint32_t serviceMaxPayload[LG_NET_SERVICE_USB + 1U];
uint32_t serviceMaxDatagram[LG_NET_SERVICE_USB + 1U];
@@ -363,6 +367,8 @@ static bool networkConnectionHandleCapability(NetworkConnection * this,
this->receivedServices |= UINT16_C(1) << capability.service;
if (capability.flags & LG_NET_CAPABILITY_RELIABLE)
this->reliableServices |= UINT16_C(1) << capability.service;
if (capability.flags & LG_NET_CAPABILITY_DATAGRAM)
this->datagramServices |= UINT16_C(1) << capability.service;
this->serviceMaxPayload[capability.service] =
capability.maxPayloadLength;
this->serviceMaxDatagram[capability.service] =
@@ -529,10 +535,12 @@ static bool networkConnectionDecodeDatagram(NetworkConnection * this,
uint64_t sessionEpoch;
uint16_t receivedServices;
uint16_t datagramServices;
uint32_t serviceMaxDatagram;
LG_LOCK(this->lock);
sessionEpoch = this->sessionEpoch;
receivedServices = this->receivedServices;
datagramServices = this->datagramServices;
serviceMaxDatagram =
this->serviceMaxDatagram[packet->envelope.service];
LG_UNLOCK(this->lock);
@@ -540,6 +548,7 @@ static bool networkConnectionDecodeDatagram(NetworkConnection * this,
return atomic_load_explicit(
&this->sessionValid, memory_order_acquire) &&
(receivedServices & (UINT16_C(1) << packet->envelope.service)) &&
(datagramServices & (UINT16_C(1) << packet->envelope.service)) &&
serviceMaxDatagram &&
packet->envelope.headerSize <= NETWORK_MAX_HEADER_SIZE &&
packet->payloadLength <= serviceMaxDatagram &&
@@ -826,6 +835,21 @@ static QUIC_STATUS QUIC_API networkConnectionCallback(HQUIC handle,
return QUIC_STATUS_SUCCESS;
}
case QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED:
LG_LOCK(this->lock);
this->datagramSendEnabled =
event->DATAGRAM_STATE_CHANGED.SendEnabled != FALSE;
this->maxDatagramSendLength =
event->DATAGRAM_STATE_CHANGED.MaxSendLength;
LG_UNLOCK(this->lock);
return QUIC_STATUS_SUCCESS;
case QUIC_CONNECTION_EVENT_DATAGRAM_SEND_STATE_CHANGED:
if (QUIC_DATAGRAM_SEND_STATE_IS_FINAL(
event->DATAGRAM_SEND_STATE_CHANGED.State))
free(event->DATAGRAM_SEND_STATE_CHANGED.ClientContext);
return QUIC_STATUS_SUCCESS;
case QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED:
return networkConnectionCertificate(this, event);
@@ -1045,6 +1069,7 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
this->connecting = true;
this->sessionEpoch = 0;
this->sendSequence = 0;
this->datagramSendSequence = 0;
this->receiveSequence = 0;
this->helloAcknowledged = false;
this->authenticationComplete = false;
@@ -1054,8 +1079,11 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
this->receivedCapabilities = 0;
this->receivedServices = 0;
this->reliableServices = 0;
this->datagramServices = 0;
this->maxPayloadLength = NETWORK_MAX_CONTROL_PAYLOAD;
this->maxDatagramPayload = 0;
this->maxDatagramSendLength = 0;
this->datagramSendEnabled = false;
memset(this->serviceMaxPayload, 0, sizeof(this->serviceMaxPayload));
memset(this->serviceMaxDatagram, 0, sizeof(this->serviceMaxDatagram));
this->receiveSize = 0;
@@ -1188,6 +1216,27 @@ bool networkConnectionValid(const NetworkConnection * this)
&this->sessionValid, memory_order_acquire);
}
bool networkConnectionServiceAvailable(NetworkConnection * this,
LGNetService service, LGNetCapabilityFlags required)
{
if (!networkConnectionValid(this) ||
service <= LG_NET_SERVICE_CORE || service > LG_NET_SERVICE_USB ||
required & ~(LG_NET_CAPABILITY_RELIABLE |
LG_NET_CAPABILITY_DATAGRAM))
return false;
LG_LOCK(this->lock);
const uint16_t serviceMask = UINT16_C(1) << service;
const bool available =
(this->receivedServices & serviceMask) &&
(!(required & LG_NET_CAPABILITY_RELIABLE) ||
(this->reliableServices & serviceMask)) &&
(!(required & LG_NET_CAPABILITY_DATAGRAM) ||
(this->datagramServices & serviceMask));
LG_UNLOCK(this->lock);
return available;
}
LG_TransportStatus networkConnectionSend(NetworkConnection * this,
LGNetService service, uint16_t messageType, uint16_t serviceVersion,
LGNetEnvelopeFlags flags, uint64_t componentEpoch, uint64_t requestID,
@@ -1201,3 +1250,76 @@ LG_TransportStatus networkConnectionSend(NetworkConnection * this,
payload, payloadLength, false) ?
LG_TRANSPORT_OK : LG_TRANSPORT_ERROR;
}
LG_TransportStatus networkConnectionSendDatagram(NetworkConnection * this,
LGNetService service, uint16_t messageType, uint16_t serviceVersion,
LGNetEnvelopeFlags flags, uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength, bool discardIfBlocked)
{
if (!this || !networkConnectionValid(this))
return LG_TRANSPORT_DISCONNECTED;
if ((payloadLength && !payload) ||
!lgNetServiceKnown(service) || service == LG_NET_SERVICE_CORE ||
!lgNetMessageKnown(service, messageType) ||
serviceVersion != networkServiceVersion(service) ||
flags & (LG_NET_ENVELOPE_RELIABLE | LG_NET_ENVELOPE_DATAGRAM |
LG_NET_ENVELOPE_EARLY_DATA))
return LG_TRANSPORT_ERROR;
const size_t packetSize = LG_NET_ENVELOPE_WIRE_SIZE +
payloadLength;
struct NetworkSend * send = malloc(sizeof(*send) + packetSize);
if (!send)
return LG_TRANSPORT_ERROR;
LGNetEnvelope envelope;
lgNetEnvelopeInit(&envelope, service, messageType, serviceVersion,
LG_NET_MESSAGE_VERSION_CURRENT);
envelope.flags = flags | LG_NET_ENVELOPE_DATAGRAM |
LG_NET_ENVELOPE_FINAL;
envelope.payloadLength = payloadLength;
envelope.componentEpoch = componentEpoch;
envelope.requestID = requestID;
LG_LOCK(this->lock);
const uint16_t serviceMask = UINT16_C(1) << service;
HQUIC connection = this->connection;
if (!connection ||
!atomic_load_explicit(&this->sessionValid, memory_order_acquire) ||
!(this->receivedServices & serviceMask) ||
!(this->datagramServices & serviceMask) ||
!this->datagramSendEnabled ||
payloadLength > this->serviceMaxDatagram[service] ||
packetSize > this->maxDatagramSendLength ||
this->datagramSendSequence == UINT64_MAX)
{
LG_UNLOCK(this->lock);
free(send);
return LG_TRANSPORT_UNAVAILABLE;
}
envelope.sessionEpoch = this->sessionEpoch;
envelope.sequence = ++this->datagramSendSequence;
if (!lgNetPacketEncode(send->data, packetSize, &envelope, payload))
{
LG_UNLOCK(this->lock);
free(send);
return LG_TRANSPORT_ERROR;
}
send->buffer.Buffer = send->data;
send->buffer.Length = (uint32_t)packetSize;
QUIC_SEND_FLAGS sendFlags = QUIC_SEND_FLAG_PRIORITY_WORK;
if (discardIfBlocked)
sendFlags |= QUIC_SEND_FLAG_CANCEL_ON_BLOCKED;
const QUIC_STATUS status = this->api->DatagramSend(connection,
&send->buffer, 1, sendFlags, send);
LG_UNLOCK(this->lock);
if (QUIC_FAILED(status))
{
free(send);
return LG_TRANSPORT_ERROR;
}
return LG_TRANSPORT_OK;
}

View File

@@ -66,8 +66,15 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * connection,
void networkConnectionCancel(NetworkConnection * connection);
void networkConnectionDisconnect(NetworkConnection * connection);
bool networkConnectionValid(const NetworkConnection * connection);
bool networkConnectionServiceAvailable(NetworkConnection * connection,
LGNetService service, LGNetCapabilityFlags required);
LG_TransportStatus networkConnectionSend(NetworkConnection * connection,
LGNetService service, uint16_t messageType, uint16_t serviceVersion,
LGNetEnvelopeFlags flags, uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength);
LG_TransportStatus networkConnectionSendDatagram(
NetworkConnection * connection, LGNetService service,
uint16_t messageType, uint16_t serviceVersion,
LGNetEnvelopeFlags flags, uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength, bool discardIfBlocked);