Revision history for pyModbusTCP

0.4.0a2 2026-10-17 (alpha: the command line tools, their options and output, may still change before 0.4.0)

    Changes since 0.4.0a1 (see 0.4.0a1 below for the changes since 0.3.1).

    Breaking changes:

    - extras: "pyModbusTCP[tools]" is renamed "pyModbusTCP[cli]" (the mbtcp-* commands of pyModbusTCP-cli).
    - ModbusServer: max_connections_per_host (16) is active by default: a client host (an IPv4 address, or a /64
                    network for IPv6) can't open more connections, the next ones are closed at once. Clients
                    behind a NAT share the same address: raise it, or set it to None to get the previous behavior.
    - ModbusServer: the constructor checks host (a str) and port (an int from 0 to 65535): TypeError or ValueError,
                    a bad value was only seen by start().
    - ModbusClient, DataBank: numpy booleans (numpy.bool_, like the items of a numpy bool array) are no longer
                    accepted as coil values (TypeError, like NumPy 2 that refuses them as integers): convert them
                    with bool() or tolist(). Numpy integers are still accepted.
    - tools: mbtcp-client exits with status 130 when Ctrl-C interrupts a request (it was 0, a success). Ctrl-C is
             still the normal end of a read repeated with -i (status 0).
    - tools: the delays given on the command line (-t, -e, -i, --idle-timeout) must be greater than 0 and at most
             86400 s (one day), the serial rate (-b) greater than 0, and the slave address of mbtcp-rtu2tcp (-a)
             from 1 to 247: invalid values are refused at startup (status 2).

    Security:

    - fix tools: mbtcp-tcp2rtu serves the waiting requests in turn by client host (round robin), instead of one
                 FIFO of 5 requests: 3 hosts (with 2 pending requests each) could fill it, every request of the
                 other hosts got exception 0x0A (denial of service). A request now waits for at most one request of
                 each other host. Up to 16 requests can wait for the serial line (the limit of 2 per host is kept).
    - fix tools: mbtcp-tcp2rtu wrote the requests with an invalid function code (0, or 0x80 to 0xFF: the exception
                 flag of a reply) on the serial line (out of --read-only mode): they get exception 0x01 now.
    - fix tools: mbtcp-tcp2rtu no longer sends on the serial line a request that its client session doesn't wait for
                 anymore (gateway timeout).
    - fix ModbusServer: request_timeout is a limit for the whole request (MBAP header and PDU) from its first byte, as
                        documented: the delay restarted for the PDU, a client could hold a session twice as long.
    - fix ModbusServer: on Windows, the listening port is reserved with SO_EXCLUSIVEADDRUSE instead of SO_REUSEADDR,
                        which allowed another local socket to bind the same port and take the connections of the
                        server (port hijacking). No change on the other systems.
    - improve ModbusServer: add max_connections_per_host (default 16), a single host can no longer take every
                            connection of max_connections (denial of service).

    Changes:

    - tools: mbtcp-tcp2rtu limits the pending requests of a client host (at most 2, the next ones get exception
      0x0A). Its refused requests are logged once per 10 s at most (with their count) instead of one line per
      request. New options --max-connections and --max-connections-per-host for mbtcp-tcp2rtu,
      --max-connections-per-host for mbtcp-server.
    - tools: mbtcp-tcp2rtu and mbtcp-rtu2tcp stop cleanly on SIGTERM (systemd, docker stop...), like mbtcp-server.
    - fix tools: -b 0 made mbtcp-tcp2rtu and mbtcp-rtu2tcp crash (ZeroDivisionError), a negative or "nan" timeout
                 of mbtcp-tcp2rtu made every request fail, "-a 0" made mbtcp-rtu2tcp a silent slave, "-i inf" made
                 mbtcp-client crash (OverflowError).
    - fix utils: valid_host() accepted some non-ASCII chars (like the Kelvin sign "\u212a", taken for a "k" by a
                 case-insensitive match), the resolver could then raise a UnicodeError at connect time.
    - fix ModbusClient: a host name that the resolver can't encode (UnicodeError) is a connect error
                        (MB_CONNECT_ERR), the request raised the exception.
    - fix ModbusServer: start() no longer leaves the listening socket open when bind() raises something else than
                        an OSError (like an OverflowError for a port changed to 70000 after the constructor).
    - fix ModbusServer: a data handler that returns a value that can't be encoded in the response (like a register
                        value out of 0-65535, or an exception code over 255) gets a SLAVE_DEVICE_FAILURE (0x04)
                        exception, the session was closed.
    - typing: type hints use the Python 3.10 syntax (X | None, list[int]...).
    - build: tools/build_dist.sh -t (install test) ran "python -m pyModbusTCP.cli" (no such module) instead of
      pyModbusTCP.tools, it also checks the release date of the CHANGES entry and the pre-release pins of the docs.
    - build: tools/code_audit.sh runs the tools of the venv (python -m, never from the PATH), from any directory,
      with a summary and an exit status (1 if a check fails), new options -a (with the tests), -s and -o.
    - tests: type hints on the tests, "mypy --strict tests" passes.

0.4.0a1 2026-10-15 (alpha: the command line tools, their options and output, may still change before 0.4.0)

    Breaking changes:

    - DataBank: remove deprecated class methods (get_bits, set_bits, get_words and set_words).
    - DataBank: set_*() methods raise a TypeError on non-integer items and a ValueError on register values out of
                the 0-65535 range (values were silently converted before, see below).
    - ModbusClient: non-integer arguments raise a TypeError, custom_request() checks its pdu argument
                    (TypeError/ValueError) and auto_open, auto_close and no_delay only accept bool or int values.
    - ModbusClient: the function code of a reply must match the request (or with the exception flag), else the
                    request fails with MB_RECV_ERR (this also applies to custom_request()).
    - ModbusServer: idle_timeout (300 s) and max_connections (64) are active by default: an idle client session is
                    closed after 5 min (its next request fails once, then the client reconnects), set them to None
                    to get the previous behavior.
    - ModbusServer: start() raises a NetworkError if the server is already running (the call was silently ignored).
    - ModbusServer: the internal request handler class ModbusService is replaced by ModbusRequestHandler and
                    CustomThreadingTCPServer, is_run is deprecated (use is_running).
    - examples: client_serial_gw.py and server_serial_gw.py are removed, replaced by the gateways of
                pyModbusTCP.tools ("python -m pyModbusTCP.tools tcp2rtu/rtu2tcp", or the commands mbtcp-tcp2rtu and
                mbtcp-rtu2tcp of pyModbusTCP-cli).

    Changes:

    - docs: README.rst converted to Markdown (README.md), like the other docs of the repository.
    - docs: Sphinx documentation rewritten: up to date quick start, user guides for the client (connection, errors,
      threads), the server (data bank, change callbacks, custom handler, access restrictions) and the data conversion
      functions, new pages for the command line tools (with their generated --help), the security policy, the
      changelog and the constants, every example documented. tools/build_docs.sh builds it (HTML, PDF...) in a
      temporary venv, the docs build is checked by the CI (warnings are errors).
    - pyModbusTCP.client and pyModbusTCP.server are now packages (one module per concern: main class, Modbus
      functions, TCP layer, argument checks...) with the same layout for both. No change for the users: same
      imports, same logger names, nested names kept (ModbusServer.PDU, ModbusServer.NetworkError...). Exceptions
      of the server are now shown as pyModbusTCP.server.errors.NetworkError (and so on) in tracebacks.
    - new package pyModbusTCP.tools: command line tools (Modbus/TCP server and client, TCP to RTU and RTU to TCP
      gateways), run them with "python -m pyModbusTCP.tools {server,client,tcp2rtu,rtu2tcp}". The gateways need
      pyserial, a new optional dependency: "pip install pyModbusTCP[serial]".
    - new package pyModbusTCP-cli (directory of the same name, no code): installs the tools as the commands
      mbtcp-server, mbtcp-client, mbtcp-tcp2rtu and mbtcp-rtu2tcp, with pyserial. Install it with
      "pip install pyModbusTCP[tools]" or "pipx install pyModbusTCP-cli". A plain "pip install pyModbusTCP" puts
      no command in the PATH and still has no dependency.
    - build: migrate from setup.py to pyproject.toml, with the hatchling build backend (no MANIFEST.in anymore, tests
      are no longer in the sdist).
    - fix ModbusClient: is_open is now False only for a closed socket (file descriptor 0 is valid).
    - fix ModbusClient: non-integer arguments (float, str...) now raise a TypeError instead of an uncaught struct.error.
    - fix ModbusClient: treat socket.gaierror as MB_CONNECT_ERR (like any other connect error).
    - fix ModbusClient: timeout is now a limit for the whole request (send + receive) and no longer for each socket I/O.
                        A server sending its response byte by byte could hold the client much longer than timeout.
    - fix ModbusClient: validate write_multiple_coils items to reject non-integer values (like 'a', None, [], 1.5),
                        matching write_single_coil behavior.
    - fix ModbusClient: write_multiple_coils() and write_multiple_registers() now set last_error (MB_RECV_ERR) when the
                        server reply does not match the request, they returned False with last_error == MB_NO_ERR.
    - fix ModbusClient: close the connection on an invalid MBAP header in a reply (the stream can't be trusted).
    - fix ModbusClient: use an incremental MBAP transaction ID (from a random start value) instead of a random one at
                        each request, a late reply to the previous request is always detected.
    - fix ModbusClient: avoid an AttributeError in __del__() when __init__() fails.
    - fix ModbusServer: close the session on send timeout (a partial send leaves the TCP stream out of sync).
    - fix ModbusServer.PDU: is_valid property was inverted.
    - fix ModbusServer: an unexpected exception during a request (like one raised by a user callback) is now logged and
                        answered with a SLAVE_DEVICE_FAILURE (0x04) exception instead of dropping the TCP session.
    - fix ModbusServer: a malformed request PDU (too short...) is answered with ILLEGAL DATA VALUE (0x03) and a request
                        with a function code >= 0x80 with ILLEGAL FUNCTION (0x01), the session was closed before.
    - fix ModbusServer: a data handler that returns a wrong number of items gets a SLAVE_DEVICE_FAILURE (0x04).
    - fix ModbusServer: start() no longer modifies class attributes of socketserver, since ThreadingTCPServer can be
                        shared with the rest of the process.
    - fix ModbusServer: close the listening socket when start() fails to bind.
    - fix ModbusServer: make function Write/Read Multiple Registers (0x17) atomic to prevent writing when the read range
                        is out of bounds.
    - fix ModbusServer: ensure Write Single Coil (0x05) complies fully with the Modbus specification.
    - fix ModbusServer: for functions 0x0F, 0x10 and 0x17 the byte count must exactly match the quantity.
    - fix ModbusServer: Read Device Identification (0x2B/0x0E) compliance with the spec (stream access restarts at the
                        first object of the category, an unknown object in individual access gets exception 0x02).
    - fix ModbusServer: support MBAP header length from 2 to 254.
    - fix ModbusServer: limit client info to (address, port) for IPv6 peers.
    - fix DataBank: set_coils(), set_discrete_inputs(), set_holding_registers() and set_input_registers() now check
                    their input. Non-integer items (float, str, None...) raise a TypeError (numpy integers and
                    booleans are accepted), and register values out of the 0-65535 range raise a ValueError.
                    Before, they were silently converted (a value like 70000 or -1 was truncated to 16 bits).
    - fix utils: valid_host() returns False for an empty host or a host with a NUL char, it raised an IndexError or a
                 ValueError (so ModbusClient(host='') raised an IndexError instead of a ValueError).
    - improve ModbusClient: decode/encode registers and bits in one pass (faster reads and writes).
    - improve ModbusClient: set TCP_NODELAY on connect (new no_delay argument, on by default) to reduce latency on small
                            request/response frames.
    - improve ModbusClient: improve socket lifecycle, parameter validation, and timeout safety.
    - improve ModbusClient: add context manager support (with ModbusClient(...) as c:), closes the connection on exit.
    - improve ModbusServer: decode/encode registers and bits in one pass for requests 0x01, 0x02, 0x0F, 0x10 and 0x17
                            (like the client), instead of one Python loop iteration per bit or register.
    - improve ModbusServer: stop() is now faster, the main loop polls the stop request every 50 ms instead of 500 ms.
    - improve ModbusServer: stop() now closes the active sessions and waits for the server thread.
    - improve ModbusServer: add request_timeout (default 30 s): a client session with a stalled request (incomplete MBAP
                            header or PDU) is now closed, it was kept (with its thread) until the client closed it.
                            Set request_timeout=None to get the previous behavior.
    - improve ModbusServer: add idle_timeout (close a session without any request for this time, default 300 s) and
                            max_connections (reject connections over this limit, default 64), None disables them.
    - improve ModbusServer: add allowed_hosts (whitelist of client IP addresses or networks, CIDR notation) and
                            read_only (refuse the write functions 0x05, 0x06, 0x0F, 0x10 and 0x17), see SECURITY.md.
    - improve ModbusServer: increase the listen backlog from 5 to 128, a burst of simultaneous connections (like clients
                            reconnecting together) no longer waits for a TCP retransmission (1 s or more).
    - improve ModbusServer: allow dynamic TCP port allocation (port=0), start() returns the bound (host, port).
    - improve ModbusServer: add is_running, wait(), active_sessions and shutdown_sessions().
    - improve ModbusServer: an exception raised by an external engine (ext_engine) is logged and closes the session.
    - improve DataBank: set_coils() and set_holding_registers() now compare the whole slice before writing, and
                        only build the list of changes when a server request needs it for the on_*_change()
                        callbacks. Writing unchanged values is much faster.
    - improve DeviceIdentification: support the "in" operator, an object value over 244 bytes raises a ValueError.
    - improve typing: type hints on the whole package and add py.typed (PEP 561).
    - add SECURITY.md (threat model, deployment recommendations, server and client protections).
    - add tools/ (benchmark, server fuzzing, code audit) and init_dev_venv.sh (test and build venv).
    - examples: add server_dynamic_port.py, add type hints to examples.

0.3.1 2026-09-29

    - fix ModbusServer: debug messages now include OSError exceptions produced by getpeername (thanks to Pär Åhlund).
    - fix ModbusClient: prevent incomplete frame transmission by using sendall (thanks to aamir-azeez).
    - updated compatibility test (python versions): remove 3.8/3.9, add 3.13/3.14.

0.3.0 2024-09-04

    - pyModbusTCP.client: now use the standard logging method as in the server part.
    - ModbusClient: debug flag is removed (see examples/client_debug.py).

0.2.2 2024-07-31

    - fix ModbusServer: wrong check of discrete inputs length in DataBank (thanks to OTnetproj).
    - updated compatibility test (python versions): remove 3.7, add 3.12.

0.2.1 2023-11-21

    - fix ModbusServer: wrong check of input registers length in DataBank (thanks to monsieurvor).
    - improve hostname validation in utils (thanks to MCXIV and schmocker).
    - experimental add of read_device_identification() (over modbus encapsulated interface) to client and server.
    - experimental add of write_read_multiple_registers() function (code 0x17) to client and server (thanks to tanj).
    - updated compatibility test (python versions): remove 3.5/3.6, add 3.11.

0.2.0 2022-06-05

    - ModbusClient: parameters are now properties instead of methods (more intuitive).
    - ModbusClient: now TCP auto open mode is active by default (auto_open=True, auto_close=False).
    - ModbusClient: add custom_request method for send user define PDU to modbus server.
    - ModbusClient: remove RTU mode.
    - ModbusClient: clarify some things (private methods and internal vars rename).
    - ModbusServer: big redesign to improve readability and maintainability.
    - ModbusServer: no longer use hard linked class DataBank (it now return a deprecation warn).
    - ModbusServer: add of ModbusServerDataHandler and DefaultDataBank class for easy customize.
    - add or update server examples for new modbus server design.
    - some updates on tests, fix random arrays generation on client test part.
    - clarify usage of root privilege for open tcp/502 (avoid Errno 13) in server example.
    - python 2 end of support.

0.1.10 2021-03-02

    - word_list_to_long() and long_list_to_word(), now support 64 bits long long with opt long_long.
    - encode_ieee() and decode_ieee(), now support double-precision format with opt double.
    - add shortcut alias for functions with long names in utils.
    - rewrite of some functions in utils.
    - improve test_utils readability.
    - server DataBank enforce type check on set data methods to avoid server crash (thanks to xuantw).
    - now get_2comp can deal with negative python int.
    - remove reference to devel github branch.
    - improve last_error_txt() and last_except_txt().

0.1.9 2021-02-26

    - add module error MB_SOCK_CLOSE_ERR (occur if frame send on close socket).
    - add modbus exception EXP_NEGATIVE_ACKNOWLEDGE (code 0x07) to constants.
    - add last_error_txt() and last_except_txt() for produce human readable status.
    - add EXP_TXT, EXP_DETAILS and MB_ERR_TXT dicts to constants (text representation of codes).
    - update of the compatibility test for python version: remove 2.6/3.2/3.3, add 3.7/3.8/3.9.
    - conform to PEP 396 (add pyModbusTCP.__version__ field).

0.1.8 2018-10-15

    - fix ModbusServer: avoid hard coded TCP port (thanks to akobyl).
    - add stop() and is_run property to ModbusServer (thanks to Rugiewitz).

0.1.7 2018-08-20

    - fix ModbusServer issue on Windows (thanks to andreascian).

0.1.6 2018-05-14

    - fix multiple TCP packets recv issue in ModbusClient and ModbusServer (thanks Farin94).

0.1.5 2017-11-23

    - add long_list_to_word to utils.
    - add float support as example.

0.1.4 2017-11-13

    - fix port and host accessors, change check now use "==" and not "is".

0.1.3 2017-09-29

    - setup now use setuptools.

0.1.2 2017-09-28

    - fix 'Rx' label on error.
    - change file mode for server.py example.
    - fix compatibility with modbus unit_id = 0 (thanks to mfkenney).
    - fix compatibility for modbus frame with garbage.

0.1.1 2016-05-30

    - add toggle_bit() to utils.
    - add server.py example.
    - add HOWTO for safe PyPI upload.

0.1.0 2016-05-30

    - fix some issues in PEP 8 conformance and inline doc.
    - client modbus RTU: move crc16 compute to utils.
    - add write_multiple_coils() function (code 0x0f).
    - add test_bit(), set_bit(), reset_bit() to utils.
    - add a modbus/TCP multithreaded server through ModbusServer class (for test only).

0.0.13 2015-12-24

    - add auto_open and auto_close to README.rst and quickstart doc.
    - add example min_read_bit.py for show minimal code approach.

0.0.12 2015-12-11

    - add auto_open and auto_close mode to ModbusClient.
    - add accessor function for socket timeout.
    - close TCP socket if hostname change
    - close TCP socket if port change

0.0.11 2015-03-27

    - fix sock.settimeout missing (thanks to bonaime).
    - fix PEP 8 style (thanks to bonaime).

0.0.10 2015-01-22

    - Add timeout parameter to ModbusClient constructor (thanks to bonaime).

0.0.9 2014-10-10

    - Fix rx_byte_count check in ModbusClient class.

0.0.8 2014-09-23

    - Catch excepts on socket send/recv.
    - Sphinx documentation include a quickstart and examples.

0.0.7 2014-08-31

    - Add unit test (see test/).
    - Add params host/port/unit_id/debug on ModbusClient constructor.
    - Add utils module for modbus data mangling.

0.0.6 2014-08-25

    - Fix "socket error" message when call open() on dual stack IPv6/4 host.
    - Check rx byte count field in functions 1 to 4.
    - Fix max bit number problem in functions 1 and 2 (from 125 to 2000).
    - Add debug message, if _send() call on close socket.
    - Rename module name from const to constants.
    - Update MANIFEST.in to remove docs and examples from sdist archive.
    - Update README.rst sample code for Python3.

0.0.5 2014-08-08

    - Now deal with IPv6 host.
    - Fix Python3 issue in _crc().
    - Improve modbus RTU receive code.
    - Secure frame size before struct.unpack invocation.

0.0.4 2014-08-07

    - Add class documentation (add doc/ and sphinx/ directory).
    - Add sphinx docstring in client.py.

0.0.3 2014-08-05

    - Fix padding problem in write_single_coil().
    - Add new examples.

0.0.2 2014-08-05

    - Compatibility with Python 3 and 2.7.
    - Use RST format for README instead of markdown, now set long_description.
    - Add a MANIFEST.in file and include examples/ on sdist.

0.0.1 2014-08-04

    - First release of pyModbusTCP.
