modbus_tcp_accept() does not apply the same socket-level options that
_modbus_tcp_connect() applies to client sockets. Specifically, the
accepted client socket is missing:
TCP_NODELAY — Nagle's algorithm remains enabled
IPTOS_LOWDELAY — low-latency TOS marking is absent (non-Windows)
- Non-blocking mode via
FIONBIO — on platforms without SOCK_NONBLOCK
Root Cause
The existing helper _modbus_tcp_set_ipv4_options() encapsulates all
three settings and is called in the client connect path
(_modbus_tcp_connect), but it is not called in the server accept path
(modbus_tcp_accept).
Observed Asymmetry
Client path (_modbus_tcp_connect):
ctx->s = socket(PF_INET, flags, 0);
// ...
rc = _modbus_tcp_set_ipv4_options(ctx->s); // TCP_NODELAY + LOWDELAY + FIONBIO
Server path (modbus_tcp_accept):
ctx->s = accept4(*s, (struct sockaddr *) &addr, &addrlen, SOCK_CLOEXEC);
// _modbus_tcp_set_ipv4_options is never called — Nagle remains active
Impact
With Nagle's algorithm active on the server side, if the TCP stack
accumulates outstanding unacknowledged data (e.g., due to partial sends
or back-to-back responses), it will hold subsequent small segments until
an ACK is received from the client. Combined with the client's Delayed
ACK mechanism (up to 40 ms per RFC 1122), this can introduce latency
in response delivery — which is particularly undesirable in Modbus TCP,
where real-time response is a protocol requirement.
Additionally, without IPTOS_LOWDELAY, server-side responses do not
receive low-latency prioritisation in QoS-aware network infrastructure.
On platforms without SOCK_NONBLOCK, the accepted socket also remains
blocking, which differs from the non-blocking mode established for
client connections.
Proposed Fix
Insert a call to _modbus_tcp_set_ipv4_options() in modbus_tcp_accept
immediately after the accepted socket is validated:
if (_modbus_tcp_set_ipv4_options(ctx->s) == -1) {
if (ctx->debug) {
fprintf(stderr,
"ERROR Failed to set IPv4 options on accepted socket %d\n",
ctx->s);
}
close(ctx->s);
ctx->s = -1;
return -1;
}
_modbus_tcp_set_ipv4_options already contains all required platform
guards (#ifndef OS_WIN32, #ifndef SOCK_NONBLOCK), so no additional
preprocessor directives are needed at the call site.
The same fix should be applied to modbus_tcp_pi_accept for
consistency with the IPv6 / protocol-independent code path.
modbus_tcp_accept()does not apply the same socket-level options that_modbus_tcp_connect()applies to client sockets. Specifically, theaccepted client socket is missing:
TCP_NODELAY— Nagle's algorithm remains enabledIPTOS_LOWDELAY— low-latency TOS marking is absent (non-Windows)FIONBIO— on platforms withoutSOCK_NONBLOCKRoot Cause
The existing helper
_modbus_tcp_set_ipv4_options()encapsulates allthree settings and is called in the client connect path
(
_modbus_tcp_connect), but it is not called in the server accept path(
modbus_tcp_accept).Observed Asymmetry
Client path (
_modbus_tcp_connect):Server path (
modbus_tcp_accept):Impact
With Nagle's algorithm active on the server side, if the TCP stack
accumulates outstanding unacknowledged data (e.g., due to partial sends
or back-to-back responses), it will hold subsequent small segments until
an ACK is received from the client. Combined with the client's Delayed
ACK mechanism (up to 40 ms per RFC 1122), this can introduce latency
in response delivery — which is particularly undesirable in Modbus TCP,
where real-time response is a protocol requirement.
Additionally, without IPTOS_LOWDELAY, server-side responses do not
receive low-latency prioritisation in QoS-aware network infrastructure.
On platforms without SOCK_NONBLOCK, the accepted socket also remains
blocking, which differs from the non-blocking mode established for
client connections.
Proposed Fix
Insert a call to
_modbus_tcp_set_ipv4_options()inmodbus_tcp_acceptimmediately after the accepted socket is validated:
_modbus_tcp_set_ipv4_optionsalready contains all required platformguards (#ifndef OS_WIN32, #ifndef SOCK_NONBLOCK), so no additional
preprocessor directives are needed at the call site.
The same fix should be applied to
modbus_tcp_pi_acceptforconsistency with the IPv6 / protocol-independent code path.