grid-x / modbus

BSD 3-Clause "New" or "Revised" License
78 stars 26 forks source link

Closing handlers and TCP connection state #48

Open StevenResiot opened 2 years ago

StevenResiot commented 2 years ago

Whenever I close a handler, any open TCP connection underneath stays open (and times out after idling - default 60seconds).

As my program dynamically opens connections to different slave servers, and as a customer is running a Modbus server that only allows one connection per IP(ugh, I know), I find myself stuck in having a single connection that customers do not always have a reference to. Additionally, I have customers wanting to access the server with different timeout settings so I'm kind of stuck as connection settings cannot really be changed after a connection is open.

Long story short: I need to open connections, do my thing, and disconnect right away. The only way I found so far is to set an IdleTimeout of a few milliseconds, but this is far from optimal.

How do I close an open TCP connection? Closing the handler doesn't close its open connections, and there's no Close method for the Client object.

Cheers

frzifus commented 2 years ago

Did you test this in an extra debug/test program or can independent/higher-level program logic cause this?

I wonder because the exposed close and internal closeIdle method of tcpTransport call the same internal close to terminate the TCP connection.

net.Conn close says:

// Close closes the connection.
// Any blocked Read or Write operations will be unblocked and return errors.
Close() error
StevenResiot commented 2 years ago

I'm confident in my testing. I create a handler, defer handler.Close(), and return modbus.NewClient(handler) after checking handler.Connect() doesn't return an error. Now I have a closed handler, a derived connection from it which works for a minute but no way to Close() it.

frzifus commented 2 years ago

Not sure if i understood your control flow correctly.

Does it look like this? ```golang func newModbusClient() modbus.Client { h := modbus.NewTCPClientHandler("127.0.0.1:502") if err := h.Connect(); err != nil { panic(err) } defer h.Close() return modbus.NewClient(h) } func main() { c := newModbusClient() if _, err := c.WriteSingleRegister(0xFF, 0xFF); err != nil { panic(err) } } ```

would be good if you could share some code and logs if available.

frzifus commented 2 years ago

Is this still a thing @StevenResiot?

I create a handler, defer handler.Close(), and return modbus.NewClient(handler) after checking handler.Connect() doesn't return an error.

My guess is that your defer handler.Close() call will close the handler like in the example.

cc @Carelo @guelfey

guelfey commented 2 years ago

@StevenResiot a minimal reproducing example code would indeed be very useful here, without this I don't know how to proceed here