Its common for the C services to be the back end services. The 2 common use cases are
If we assume that the C-service is fairly atomic, it works or it doesn't and does not partially work, then for one-way handling we just need some code to...
If invoking the C service fails (such that we know it wasn't invoked), we could automatically retry a certain number of times before we acknowledge the message.
The problem is if the above code were to be killed before the message is acknowledged, we'd invoke the service again.
To get around this we could persist that we have invoked the service, so that if we are killed we would not invoke the service again but put the message on some dead letter queue for manual reconciliation.
Another approach could be for the C service to say whether or not it has successfully processed the message before. This just pushes the problem inside the C code requiring that it persists when things are invoked and when things complete so that it can know when duplicate messages are delivered.
This scenario is as above but rather than just acknowledge the inbound message we wish to send a reply and acknowledge the inbound message. So this could be regarded as a small JMS transaction.
Again if the C service is capable of knowing if it has seen the message before then we can avoiid step 3.
We can link C into a Java process and invoke it directly via JNI. Another option is to wrap the C code as an Apache module and perform a HTTP POST to invoke a C service and extract the results of the service.