-
Notifications
You must be signed in to change notification settings - Fork 244
Description
Hello,
First, thank you for creating and maintaining this excellent and high-performance Modbus library.
The Issue
I am using the library to communicate with an industrial controller (PLC) that has a very strict implementation of the Modbus/TCP protocol.
When I send multiple requests asynchronously in a rapid succession, the library's underlying Netty channel batches them into the payload of a single TCP packet. My Wireshark captures clearly show multiple Modbus ADUs being sent in one TCP segment.
This behavior violates the official Modbus specification, which states, "A TCP frame must transport only one MODBUS ADU." As a result, the controller considers this a framing error and discards the packet, so I never receive a response.
What I've Tried
TCP_NODELAY: I have confirmed that settingChannelOption.TCP_NODELAYtotruedoes not solve the problem. The batching appears to happen at the application/Netty level before the data is handed to the OS network stack, so Nagle's algorithm is not the root cause. Or the channel option is not applied properly, I can't really tell.
What I'm basically doing is the following:
val config = NettyTcpClientTransport.create { cfg ->
cfg.hostname = uri.host
cfg.port = uri.port
cfg.connectPersistent = device.holdConnection
cfg.connectTimeout = device.timeout
cfg.bootstrapCustomizer = Consumer<Bootstrap> { it.option(ChannelOption.TCP_NODELAY, config.tcpNoDelay) }
}- Sequential Chaining: The only effective workaround is to manually syncronize requests by guarding the
ModbusTcpClientinstance with a semaphore and force sequential request / response behaviour.
The Problem with the Workaround
While manually synchronization works for ensuring compliance, it forces a sequential execution model. This makes the application code more complex and negates the throughput benefits of sending multiple independent requests concurrently in an asynchronous manner.
Unfortunately otherwise the controller does not respond in a predictable way... Sometimes I simply does not answer at all.
Is there any similar problem you are aware of? Would it be possible to add a configuration option to the client to handle this common industrial requirement more directly?
A configuration flag—perhaps something like .strictComplianceMode(true) or .flushAfterRequest(true)—would be incredibly helpful. When enabled, this mode would ensure that the library flushes the channel after each request is written, guaranteeing one ADU per TCP packet without forcing the user to implement complex sequential logic.
Or is there anything I'm missing here?
Thank you for your time and consideration.
FYI:
I'm using the newest version of the client v2.1.1



