Skip to content

client.close does not close the connection #2617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
paranoid64 opened this issue Mar 25, 2025 · 7 comments
Closed

client.close does not close the connection #2617

paranoid64 opened this issue Mar 25, 2025 · 7 comments

Comments

@paranoid64
Copy link

paranoid64 commented Mar 25, 2025

pymodbus verison is 3.8.6

I go through the database and get the addresses and the ip of the modbus devices and establish the connection. after the wet is saved in the DB, the connection should actually be disconnected. but unfortunately this does not happen.
Too many connections are open after approx. 5 minutes.

x.10 is the server and x.45:502 the modbus devices

tcp        0      0 x.10:53964        x.45:502          TIME_WAIT   timewait (15,60/0/0)
tcp        0      0 x.10:48414        x.47:502          TIME_WAIT   timewait (19,34/0/0)
tcp        0      0 x.10:45588        x.27:502          TIME_WAIT   timewait (25,87/0/0)

Here the Code:

includes:

from datetime import datetime, timedelta, timezone

from pymodbus.constants import Endian
#from pymodbus.payload import BinaryPayloadDecoder
import pymodbus.client as ModbusTcpClient
from pymodbus import (
    ExceptionResponse,
    FramerType,
    ModbusException,
    pymodbus_apply_logging_config,
)
async def con_modbus_device():

    start_time = time.time()
    start_timestamp=datetime.utcnow()

    cur = GL.con_mariadb.cursor(buffered=True , dictionary=True)
    table = GL.config['mariadb']['TableBMS']
    sql = f'SELECT id, ip, device, device_id FROM {table} WHERE protocol="modbus"'

    cur.execute(sql)
    res = cur.fetchall()

    if not res:
        await GL.log('error',f'no modbus device found')
    else:
        for row in res:
            bmsid=row["id"]
            ip=row["ip"]
            device=row["device"]
            device_id=row["device_id"]
            await GL.log('info',f'try to establish a connection {ip}')

            if GL.config['modbus']['debug']:
                pymodbus_apply_logging_config("DEBUG")

            try:
                client = ModbusTcpClient.AsyncModbusTcpClient(ip,port=GL.config['modbus']['port'],framer=FramerType.SOCKET, timeout=2)
                connection = await client.connect()

                if connection:
                    table = GL.config['mariadb']['TableBMSFields']
                    sql = f'SELECT id, field, adress, actual, units_state FROM {table} WHERE bmsid="{bmsid}"'

                    cur.execute(sql)
                    res2 = cur.fetchall()
                    for row2 in res2:

                        fieldid=row2["id"]
                        field=row2["field"]
                        adress=row2["adress"]

                        try:
                            rr = await client.read_holding_registers(int(adress), count=2, slave=1)
                            num = client.convert_from_registers(rr.registers, client.DATATYPE.FLOAT32)
                            value = float('{0:.2f}'.format(num))

                            end_timestamp=datetime.utcnow()
                            interval = await measuring_interval_start(start_timestamp) #(15 min. measuring interval)
                            await insert_value(field, value, start_timestamp, interval, bmsid)

                            if rr.isError():
                                await GL.log('error',f"Received Modbus library error({rr})")
                            if isinstance(rr, ExceptionResponse):
                                await GL.log('error',f"Received Modbus library exception ({rr})")

                        except ModbusException as exc:
                            await GL.log('error',f"Received ModbusException({exc}) from library")
                    client.close()
                    await GL.log('info',f'Connection to the device {ip} successful')
                else:
                    print('client connection lost')

            except ModbusException as e:
                await GL.log('error',f'Connection to the device {ip} faild')
                await GL.log('error',f'{e}')
                pass

What am I doing wrong that client.close() does not work?

@janiversen
Copy link
Collaborator

The sequence of calls looks ok.

As you can see in the pymodbus code, close() calls the asyncio close, so that seems correct. I have not heard of problems with asyncio not closing sockets.

It is known that some OS keep the socket active for a while, basically until the OS runs the central cleanup process...so maybe your problem is there.

However you must be opening a lot of connections to make this happens (or have a very limited OS). One of my raspberry pi have 100+ connect active, and when testing on my mac mini I use 2.000+ connections.

@paranoid64
Copy link
Author

there are about 240 modbus devices and there is also bacnet and thermo devices via mqtt. I don't have this problem with MQTT and Bacnet. I can only see that the connections are kept open as you have described. but with each run I have twice as many connections, so I am very quickly over 2000 connections. At some point it just comes to an end. I'm still looking to see if it's debian problem.

@janiversen
Copy link
Collaborator

When you say "with each run" I suppose it includes a new python instance or at at least a new start of the python program....in this case it is clearly outside pymodbus/python.

If you do not restart python it could be the python garbage collect does not run often enough, I have seen similar problems with python 3.9. Please do not forget you make a new client object for each connection, so pymodbus is "restarted" with each client object creation.

@janiversen
Copy link
Collaborator

Try to add "del client" and the end of your loop to force it out.

@paranoid64
Copy link
Author

so del client is in, but unfortunately it doesn't change anything. The TCP connection is not closed. With TCP, there must be a clean separation on both sides, i.e. my software and the device must terminate the TCP connection cleanly. Otherwise it takes forever for the connection to disconnect. I have tested this with TCP socket, the modbus devices close the connection correctly.

@paranoid64
Copy link
Author

i have now grouped the ip's with sql join and make a connection for this ip and all modbus adresses. this reduces the problem somewhat. then i have a 60 second break in it, until then all connections are closed.

@janiversen
Copy link
Collaborator

Just for info I have tested this specifically on a mac mini with python 3.13. Wireshark shows the FIN packet being sent correctly and netstat shows that the socket is terminated, so pymodbus/asyncio works correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants