libs/libc/netdb: Optimize the timeout calculation logic:

1. Adopt an exponential backoff strategy () to dynamically adjust the timeout
   duration, which is suitable for retry scenarios.
2. Optimize the default configuration to set the initial timeout to 5 seconds.
3. Support dynamic modification of the maximum timeout limit to adapt
   to different scenarios.
Reference: RFC 1536 (section on retransmission implementation recommendations)

Signed-off-by: nuttxs <zhaoqing.zhang@sony.com>
This commit is contained in:
nuttxs 2025-08-05 17:56:14 +08:00 committed by Xiang Xiao
parent c506c25f19
commit 7b3913da60
4 changed files with 45 additions and 12 deletions

View file

@ -140,7 +140,7 @@ config NETDB_DNSCLIENT_MAXRESPONSE
config NETDB_DNSCLIENT_RECV_TIMEOUT
int "DNS receive timeout"
default 30
default 5
---help---
This is the timeout value when DNS receives response after
dns_send_query, unit: seconds
@ -152,6 +152,15 @@ config NETDB_DNSCLIENT_SEND_TIMEOUT
This is the timeout value when DNS send request on dns_send_query,
unit: seconds
config NETDB_DNSCLIENT_MAX_TIMEOUT
int "DNS maximum timeout"
default 30
---help---
Maximum timeout value in seconds when using exponential backoff
for DNS retries. The timeout grows exponentially with each retry
(base_timeout * 2^retry_count) but is capped at this maximum value.
Set to 0 to disable the cap.
config NETDB_DNSCLIENT_RETRIES
int "Number of retries for DNS request"
default 3

View file

@ -162,7 +162,9 @@ void dns_restorelock(unsigned int count);
* server. The name server was previously selected via dns_server().
*
* Input Parameters:
* None
* family - Address family (AF_INET or AF_INET6)
* stream - Whether to use stream socket
* retry_count - Current retry attempt (0 for first attempt)
*
* Returned Value:
* On success, the bound, non-negative socket descriptor is returned. A
@ -170,7 +172,7 @@ void dns_restorelock(unsigned int count);
*
****************************************************************************/
int dns_bind(sa_family_t family, bool stream);
int dns_bind(sa_family_t family, bool stream, int retry_count);
/****************************************************************************
* Name: dns_query

View file

@ -55,7 +55,8 @@
* server. The name server was previously selected via dns_server().
*
* Input Parameters:
* None
* retry_count - Current retry attempt (0 for first attempt)
* stream - Whether to use stream socket
*
* Returned Value:
* On success, the bound, non-negative socket descriptor is returned. A
@ -63,12 +64,30 @@
*
****************************************************************************/
int dns_bind(sa_family_t family, bool stream)
int dns_bind(sa_family_t family, bool stream, int retry_count)
{
int stype = stream ? SOCK_STREAM : SOCK_DGRAM;
struct timeval tv;
int sd;
int ret;
int timeout_sec;
/* Calculate progressive timeout: (base timeout * 2^retry_count)
* For retry_count 0: base timeout, 1: base*2, 2: base*4, etc.
*/
timeout_sec = CONFIG_NETDB_DNSCLIENT_RECV_TIMEOUT;
if (retry_count > 0)
{
/* Apply exponential backoff with configurable maximum */
timeout_sec = timeout_sec << retry_count;
if (CONFIG_NETDB_DNSCLIENT_MAX_TIMEOUT > 0 &&
timeout_sec > CONFIG_NETDB_DNSCLIENT_MAX_TIMEOUT)
{
timeout_sec = CONFIG_NETDB_DNSCLIENT_MAX_TIMEOUT;
}
}
/* Create a new socket */
@ -80,17 +99,17 @@ int dns_bind(sa_family_t family, bool stream)
return ret;
}
/* Set up a receive timeout */
/* Set up a receive timeout with progressive strategy */
tv.tv_sec = CONFIG_NETDB_DNSCLIENT_RECV_TIMEOUT;
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
ret = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));
if (ret >= 0)
{
/* Set up a send timeout */
/* Set up a send timeout (same progressive strategy) */
tv.tv_sec = CONFIG_NETDB_DNSCLIENT_SEND_TIMEOUT;
tv.tv_sec = timeout_sec;
tv.tv_usec = 0;
ret = setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, &tv,

View file

@ -854,20 +854,23 @@ static int dns_query_callback(FAR void *arg, FAR struct sockaddr *addr,
bool stream = false;
/* Loop while receive timeout errors occur and there are remaining
* retries.
* retries. Use progressive timeout strategy.
*/
for (retries = 0; retries < CONFIG_NETDB_DNSCLIENT_RETRIES; retries++)
{
bool should_try_stream;
ninfo("INFO: DNS query retry %d/%d\n",
retries + 1, CONFIG_NETDB_DNSCLIENT_RETRIES);
try_stream:
#ifdef CONFIG_NET_IPv6
if (dns_is_queryfamily(AF_INET6))
{
/* Send the IPv6 query */
sd = dns_bind(addr->sa_family, stream);
sd = dns_bind(addr->sa_family, stream, retries);
if (sd < 0)
{
query->result = sd;
@ -922,7 +925,7 @@ try_stream:
{
/* Send the IPv4 query */
sd = dns_bind(addr->sa_family, stream);
sd = dns_bind(addr->sa_family, stream, retries);
if (sd < 0)
{
query->result = sd;