0

I have implemented a Modbus over TCP as server software in Python. App is multithreaded and relies heavily on standard libs. I have problems managing the connection on the server side. Meanwhile my implementation as Modbus over TCP as client works just fine.

Implementation description

  • The server is multithreaded, one thread manages the SOCK_STREAM socket for receiving frames
  • select is used out of efficiency reasons
  • A semaphore is used for preventing concurrent access on socket resource while sending or receiving
  • Encapsulation of Modbus upper layer is done transparently through send and receive methods, it is only a matter of building a frame with the right header and payload anyway...
  • Another threads runs, inside it, Modbus send and receive methods are invoked.

TCP Context

TCP is up and running, bound to a port, max client set and listening.

Traces under wireshark show:

  • Client: SYN
  • My app Server: SYN, ACK
  • Client: ACK

On the server side a brand new socket has been created as expected and bound to the client socket.

So far, all is good.

Modbus Context

  • Client: Send Modbus frame, TCP flags = 0x18 which is ACK + PUSH
  • My app Server: Does not wait and send a single empty TCP ack frame.
  • Client: Waits for a modbus frame with tcp ack flag. Therefore, takes it as an error and asks to closes the connection.

Hence, my server software cannot send any actual response afterwards as the socket on the client side is being closed or is already closed.

My problem

  • I receive a modbus frame that the main thread need to process (server side)
  • Processing takes a few ms, in the meantime a TCP ACK frame is sent through my server socket, whereas I would like it not to send anything !

Do you have any idea on how to manage the ACK behavior ? I have read stuff about the naggle algorithm, but it does not seem to be in the scope of the problem here... I'm not sure that any option of the setsockopt method would solve my problem also, but I may be mistaken.

If you have any suggestion I am very interested... I hope I am clear enough.

Chad Nouis
  • 6,861
  • 1
  • 27
  • 28
elnoonio
  • 1
  • 1
  • I'm not familiar with Modbus, but if you're writing the TCP stuff in Python, shouldn't sending/receiving ACKs be completely transparent to your code? In your Python TCP client, how do you know you received an ACK without a payload? Your recv() should just return when you have actual data. I'm not understanding why sending an empty ACK is bad and results in an error. In anycase, you can try using delayed ACKs: TCP_QUICKACK socket option. – Neal Mar 14 '12 at 13:44
  • You are right, I do not want to manage directly the TCP stack, I want it to be transparent for me. Nevertheless, I have seen with wireshark (tool for sniffing network) that my socket is sending on its own a TCP ACK without a payload. Whereas my application is still in the middle of processing the appropriate answer. – elnoonio Mar 14 '12 at 14:14
  • Sending an empty TCP ACK seems to be treated as an error case by the Modbus client, that is why I would prevent the underlying TCP stack from sending any kind of TCP ACK because of some timers – elnoonio Mar 14 '12 at 14:18
  • TCP ACK is an integral part of the TCP reliability features. If you prevent it from being sent, the client network stack will resend the last packet. Also, Modbus should live _above_ TCP, which means that it will normally not know about things like ACK packets. If the client crashes because it receives an ACK, it's doing something wrong. – Some programmer dude Mar 15 '12 at 06:19

1 Answers1

0

It seems like a strange requirement that all TCP packets must contain a payload as this is very difficult to control unless you are integrated with the TCP stack. If it really is the case that the client crashes because the ACK has no Modbus payload, I think the only thing you can do from python is try disabling the TCP_QUICKACK socket option so that TCP waits 500ms before sending an ACK. This obviously won't work in all cases (or may not at all if it takes your code > 500ms to create a response), but I don't know of any other options using the socket API from python.

This SO answer tells you how to disable it: Disable TCP Delayed ACKs. Should be easy to figure out how to enable it from that. Note, you need to constantly re-enable it after receiving data.

Community
  • 1
  • 1
Neal
  • 6,722
  • 4
  • 38
  • 31
  • Thanks for your answer, unfortunately the server is running on a windows OS. Anyhow, this parameter seems to be tunable in the registry... I will give it a try. – elnoonio Mar 14 '12 at 17:03