kochedykov / jlibmodbus

JLibModbus - is an implementation of the Modbus protocol v1.1b in java language. Java modbus library. It works. Welcome.
http://kochedykov.github.io/jlibmodbus/
Apache License 2.0
307 stars 128 forks source link

java.net.SocketTimeoutException: Read timed out #26

Closed browncrane closed 6 years ago

browncrane commented 6 years ago

The idea is to make the server listening and the clients would connect to them.
But not as a Modbus TCP client, as a normal TCP client, so the server could read the clients (act like Modbus Master) rather than read by clients. Maybe the idea totally wrong.

The test code:

    @Test
    public void testConnection() throws IOException, ModbusNumberException, ModbusProtocolException, ModbusIOException {
        ServerSocket serverSocket = new ServerSocket(3333);
        Socket client = serverSocket.accept();
        ModbusTranslator modbusTranslator = new ModbusTranslator(client);
        modbusTranslator.connect();
        modbusTranslator.setResponseTimeout(3000);
        modbusTranslator.writeSingleRegister(1,1,1);
    }

Which will issue a SocketTimeoutException when running the test. Using hercules-setup-utility from https://www.hw-group.com/software/hercules-setup-utility to simulate a client socket.

  1. The network is fine when using client.getOutputStream() and client.getInputStream();
  2. The error log is almost same if change last line to modbusTranslator.readHoldingRegisters(1,1,1);
  3. The ModbusTranslator implements FrameEventListenerList with a custom EmbeddedConnection which extends ModbusConnection

The mainly different from your ModbusMasterConnection is it get the socket instead from init from tcp parameters.

    @Override
    protected void openImpl() {
        if (!isOpened()) {
            if (socket.isConnected()) {
                try {
                    transport = ModbusTransportFactory.createTCP(socket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                setReadTimeout(getReadTimeout());
            }
        }
    }
com.intelligt.modbus.jlibmodbus.exception.ModbusIOException: java.net.SocketTimeoutException: Read timed out

    at com.intelligt.modbus.jlibmodbus.tcp.TcpAduHeader.read(TcpAduHeader.java:103)
    at com.intelligt.modbus.jlibmodbus.net.transport.ModbusTransportTCP.read(ModbusTransportTCP.java:50)
    at com.intelligt.modbus.jlibmodbus.net.transport.ModbusTransport.readMessage(ModbusTransport.java:69)
    at com.intelligt.modbus.jlibmodbus.net.transport.ModbusTransport.readResponse(ModbusTransport.java:64)
    at dems.server.ModbusTranslator.readResponse(ModbusTranslator.java:80)
    at dems.server.ModbusTranslator.processRequest(ModbusTranslator.java:100)
    at dems.server.ModbusTranslator.writeSingleRegister(ModbusTranslator.java:264)
    at dems.server.ModbusTranslatorTest.testConnection(ModbusTranslatorTest.java:19)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.net.SocketTimeoutException: Read timed out
    at java.base/java.net.SocketInputStream.socketRead0(Native Method)
    at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
    at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
    at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
    at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:345)
    at com.intelligt.modbus.jlibmodbus.net.stream.InputStreamTCP$1.read(InputStreamTCP.java:55)
    at com.intelligt.modbus.jlibmodbus.net.stream.base.LoggingInputStream.read(LoggingInputStream.java:61)
    at com.intelligt.modbus.jlibmodbus.net.stream.base.ModbusInputStream.read(ModbusInputStream.java:50)
    at com.intelligt.modbus.jlibmodbus.tcp.TcpAduHeader.read(TcpAduHeader.java:91)
    ... 29 more

Disconnected from the target VM, address: '127.0.0.1:53170', transport: 'socket'

Process finished with exit code -1

Any help would be appreciated.

kochedykov commented 6 years ago
   ServerSocket serverSocket = new ServerSocket(3333);
    Socket client = serverSocket.accept();
    ModbusTranslator modbusTranslator = new ModbusTranslator(client);
    modbusTranslator.connect();

Why do you invoke connect, if you use a connected socket? Please show the code you use to connect to serverSocket.

browncrane commented 6 years ago

In the ModbusTranslator class

    public final void connect() throws ModbusIOException {
        if (!isConnected()) {
            connectImpl();
        }
    }

    private void connectImpl() throws ModbusIOException {
        getConnection().open();
    }

The getConnection() returns a EmbeddedConnection object. The EmbeddedConnection class extends ModbusConnection and

    @Override
    protected void openImpl() {
        if (!isOpened()) {
            if (socket.isConnected()) {
                try {
                    transport = ModbusTransportFactory.createTCP(socket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                setReadTimeout(getReadTimeout());
            }
        }
    }

    @Override
    protected void closeImpl() throws ModbusIOException {
        try {
            if (transport != null) {
                transport.close();
            }
        } catch (IOException e) {
            throw new ModbusIOException(e);
        } finally {
            transport = null;
        }
    }

Thinking about the hecules TCP client, it doesn't act like a Modbus Slave, would that be the reason of the SocketTimeoutException?

kochedykov commented 6 years ago

Exactly. You should implement a TCP-client with Modbus Slave functionality. In any case, this approach looks a little weird.

browncrane commented 6 years ago

Thank you for confirming my guess. What's the better practice in your experience? The approach I'm trying is base on the device which packages serial Modbus slave data as Modbus TCP data. And it acts as a TCP client to connect to the server first. After authorized by the server the device transfer Modbus data.

kochedykov commented 6 years ago

In my experience, the procedure of a remote device registration and the procedure of data transmission are different and should be performed in the different sessions. A remote device as a TCP-client sends it's connection parameters. The device performs this procedure at power on or after changing its settings. The server stores devices' connection parameters in a database and uses it to connect to devices. The server can ping the devices to determine if a device is available or not. If not the server can mark it as temporarily unavailable or remove it from the database.