modbus icon indicating copy to clipboard operation
modbus copied to clipboard

Closing handlers and TCP connection state

Open StevenResiot opened this issue 3 years ago • 5 comments

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

StevenResiot avatar Jan 19 '22 14:01 StevenResiot

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

frzifus avatar Jan 19 '22 17:01 frzifus

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.

StevenResiot avatar Jan 24 '22 13:01 StevenResiot

Not sure if i understood your control flow correctly.

Does it look like this?
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 avatar Jan 25 '22 10:01 frzifus

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

frzifus avatar Jun 13 '22 19:06 frzifus

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

guelfey avatar Jun 14 '22 06:06 guelfey