So the application I’m working on has to handle Modbus over TCP. We already handle Modbus over serial connections, so this is a small addition.
I set up a TCPSocket
to listen on port 502 which is the normal port. I make the server socket non blocking and bind sigio()
to a callback. In the callback I accept()
the incoming client socket and make it too non blocking and bind its sigio()
to a callback. Both callbacks send an event over a Queue
where a thread then handles them.
This seems like fairly basic async setup, and one that was recommended to me in a previous question. However, the problem is that there is almost always a 3 second delay from when the client socket was accept()
:ed to the first data arriving to the client data callback. Sometimes the data arrives in a few milliseconds as I would expect it to, but in 90% or more of the cases it seems to take exactly 3000 ms from accept()
to data arriving. The delay is extremely consistent and only varies with some tens of ms. This leads me to believe I have done something wrong with the setup or that the TCP side is a bit buggy. When sending the event over the queue I register the sending and receiving times, and it seems the thread that then handles the events gets them within 0 to 15ms. The handling thread has no other tasks right now, it sits and waits for these two events only.
Below is the general setup of the code. The postTask()
function posts a function pointer to the queue and the thread then executes it. I have commented which context handles what. All error handling is removed for simplicity.
// worker thread context
void handleClientData() {
// END
nsapi_size_or_error_t status = modbusClientSocket->recv(...);
}
// ISR context, just post a task
static void clientDataCallback() {
postTask(handleClientData);
}
// worker thread context
void handleIncomingClient() {
nsapi_error_t status = NSAPI_ERROR_OK;
modbusClientSocket = modbusServerSocket.accept(&status);
// START
modbusClientSocket->set_timeout( 0 );
modbusClientSocket->set_blocking( false );
modbusClientSocket->sigio(callback(clientDataCallback));
}
// ISR context, just post a task
void incomingClientCallback() {
postTask(handleIncomingClient);
}
// main thread
void initModbusTcpAsync() {
modbusServerSocket.open(EthInterface::get_default_instance());
modbusServerSocket.bind(modbusTcpPort);
modbusServerSocket.listen(1)
modbusServerSocket.set_timeout( 0 );
modbusServerSocket.set_blocking( false );
int optval = 1;
modbusServerSocket.setsockopt(NSAPI_SOCKET, NSAPI_REUSEADDR, &optval, sizeof(optval));
modbusServerSocket.sigio(callback(incomingClientCallback));
// start accepting clients, this will give NSAPI_ERROR_WOULD_BLOCK
nsapi_error_t status;
TCPSocket * client = modbusServerSocket.accept(&status);
}
There is almost always a 3000ms delay from START
to END
. I see no reason for it. Wiresharking the traffic shows that the client application sends a packet immediately after the connection handshake, but MBed doesn’t react on it until 3000 ms later.
I also did a totally sync version where this communication is handled by a single thread without any non blocking setup, queues or sigio()
. It works pretty good, but it still takes 7-15 seconds for about 40 small 12-40 bytes packets to be sent back and forth. Not abysmal, but clearly not what it should be. However, there are no long accept delays there, the time from the initial accept()
to the first data being readable is some tens of milliseconds.
Any idea what I’m doing wrong here? The long delay never varies from 3000 ms, no matter when I do the calling, so the board isn’t busy with other things. The worked thread reacts immediately to a posted event, so I’m at a loss here.