【问题标题】:Error while reading data through socket communication通过套接字通信读取数据时出错
【发布时间】:2018-03-24 06:19:20
【问题描述】:

以下场景可以解释我的问题。
我有一个充当服务器套接字程序的 PLC。我编写了一个客户端 Java 程序,通过套接字通信与 PLC 进行通信。

在这个过程中发生的步骤是:
1) 每秒钟我的客户端程序碰巧与 PLC 通信,读取流中的数据,将数据临时存储在 ByteArrayOutputStream 中并关闭输入流和套接字。下面sn-p给出思路

    try {
        socket = new Socket(host, port);
        is = socket.getInputStream();
        outputBuffer = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024];
        int read;
        if((read = is.read(buffer)) != -1) {
            outputBuffer.write(buffer, 0, read);
        }
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            System.out.println("Before closing the socket");
            try {
                is.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("After closing the socket");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

2)根据我的要求处理存储的数据是我想要做的。所以每 1 秒,客户端程序连接到服务器,读取数据(如果数据存在),存储数据,关闭套接字并处理它。它必须持续很长时间,可能直到服务器程序启动。这可能会每隔几周发生一次。

3) 我面临的问题是,我可以运行上述节目 1-2 小时,但从那时起,客户端程序无法从服务器程序(在这种情况下为 PLC)获取数据,虽然两者都是通过套接字连接的。即存在 128 个字节的数据,但客户端程序无法读取该数据。这在程序成功运行近 2 小时后开始发生

4) 请找到可能对您有所帮助的简短代码。

public class LoggingApplication {
    public static void main(String[] args) throws NumberFormatException {
        if (args.length > 0 && args.length == 2) {
            String ipAddress = mappingService.getIpAddress();
            int portNo = (int) mappingService.getPortNo();
            ScheduledExecutorService execService = Executors.newScheduledThreadPool(1);
            execService.schedule(new MyTask(execService, ipAddress, portNo, mappingService), 1000, TimeUnit.MILLISECONDS);
        } else {
            throw new IllegalArgumentException("Please pass IPAddress and port no as arguments");
        }
    }
}

可运行代码:

public class MyTask implements Runnable {

    public ScheduledExecutorService execService;
    private String ipAddress;
    private int portNo;
    private ConfigurationMappingService mappingService;
    private MySocketSocketUtil mySocketSocketUtil;

    public MyTask(ScheduledExecutorService execService, String ipAddress, int portNo, ConfigurationMappingService mappingService) {
        this.execService = execService;
        this.ipAddress = ipAddress;
        this.portNo = portNo;
        this.mappingService = mappingService;
    }

    public void run() {
        MySocketSocketUtil mySocketSocketUtil = new MySocketSocketUtil(ipAddress, portNo);
        execService.schedule(new MyTask(execService, ipAddress, portNo, mappingService), 1000, TimeUnit.MILLISECONDS);

        mySocketSocketUtil.getData();  //It's able to fetch the data for almost 2 hours but from then, it's just getting empty data and it's keep on giving empty data from then. and so on.


        /*
         *
         *Some code
         */
    }
}

这就是我遇到问题的地方
mySocketSocketUtil.getData(); 能够获取数据近 2 小时,但从那时起,它只是获取空数据并保持从那时起提供空数据。等等。我知道这是一个大问题,我想了解可能出了什么问题。

编辑:我忽略了检查流结束并基于它关闭套接字的条件,因为我知道我只会读取前 1024 个字节的数据。所以,我在 finally 块中关闭了套接字

【问题讨论】:

    标签: java sockets


    【解决方案1】:
    socket = new Socket(host, port);
    if(socket != null && socket.isConnected())
    

    此时socket 不可能为 null 或 socket.isConnected() 为 false。不要编写无意义的代码。

            if((read = is.read(buffer)) != -1) {
                outputBuffer.write(buffer, 0, read);
            };
    

    在这里,您忽略了流的可能结束。如果read() 返回-1,您必须关闭套接字。它永远不会不会再次返回 -1。这完全解释了您的“空数据”:

    从那时起,它只是获取空数据,然后继续提供空数据,依此类推

    你应该创建一个新的Socket除非你已经收到-1或前一个套接字上的异常。

        } else {
            System.err.println("Socket couldn't be connected");
        }
    

    无法访问:见上文。不要编写无意义的代码。

    【讨论】:

    • 好的。无意义的代码,我会处理的。但这里的主要问题是,我必须每秒打开一个新套接字,因为每秒在服务器端启用一个标志,我需要读取它。如果有一个活动连接,我不能这样做,因为它是一个数据流,我不能从同一个字节读取同一个套接字。如我错了请纠正我。所以,我正在做的是,每一秒,我都在打开一个套接字,读取数据,检查标志是否启用,......,关闭套接字。如果错了,还有哪种方法更好?
    • 我不明白。 “不能为同一个套接字读取相同的字节”是没有意义的。我已经回答了你的最后一个问题:保持打开状态,直到流结束或出现异常。
    • 我已经编辑了我的问题。而且,我忽略了检查流结束并基于它关闭套接字的条件,因为我知道我只会读取前 1024 个字节的数据。如果是这样,检查 -1 是否有意义?
    • 当然检查流结束是有意义的。服务器有权随时断开您的连接。而且您 not 澄清了“不能为同一个套接字读取相同的字节”,这 not 有意义。我会说您应该断开连接,直到服务器断开连接。我还要说,您似乎违反了任何应用程序协议,只读取每条消息的 1024 个字节。
    • “不能为同一个套接字读取相同的字节”,我的意思是我不能在同一个套接字的输入流上调用读取方法。为什么我要再次调用 read 方法是因为第 0-6 字节的数据可能已更改。我想访问那个值。出于这个特定目的,我重新创建套接字、读取数据、存储数据、再次关闭套接字就是我在这里所做的。这必须每隔一两秒发生一次
    【解决方案2】:

    您永远不应断开已建立的连接。在 LoggingApplication 中连接一次。连接套接字后,将其保持打开状态。在下次读取时重用套接字。

    【讨论】:

      【解决方案3】:

      我认为在找到问题的解决方案之前,您需要解决几个问题。请先尝试遵循以下建议:

      1. 正如@EJP 所说,不需要此代码块。

        if(socket != null && socket.isConnected()) {
        
      2. 您也在使用长度为 1024 的字节数组,而不是使用 while 或 for 循环来读取数据流。您是否期望只有一个永远不会超过 1024 字节的数据块?

            byte[] buffer = new byte[1024];
            int read;
            if((read = is.read(buffer)) != -1) {
        
      3. 这也不是必需的,因为它无法访问。

        } else {
            System.err.println("Socket couldn't be connected");
        }
        
      4. 您能解释一下您所期望的数据流行为吗?

      5. 最后但并非最不重要的is.read(buffer) 是一个阻塞调用,因此如果还没有要读取的数据,它将暂停线程执行。

      请尝试回答我提出的问题。

      @KishoreKumarKorada 从您在评论部分的描述中,您似乎正在监视服务器端的数据更改。套接字流以只读方式工作。所以,

      第一件事是,您每次都需要从服务器请求,服务器需要重新发送每个请求的数据。

      其次,您呈现的方式更像是在字节级别上操作,除非您有任何正当理由这样做,否则这不是很好的方式。好的方法是以 JSON 或 XML 格式包装数据并通过流发送。但是为了减少带宽消耗,有时您可能需要对字节流进行操作。你需要做出决定。

      第三,为了监控数据的变化,更好的方法是使用一些时间戳来比较服务器端数据何时发生变化和客户端存储的时间戳是什么,如果它们匹配,则数据没有变化。否则从服务器端获取数据并更新客户端。

      第四,当有你无法读取的可用数据时,你能否调试 ins.read(...) 语句以查看它是否被执行以及执行是否进入 if 块或 if 语句被评估假的?如果为真,则检查读取值并告诉我您发现了什么?

      谢谢。

      【讨论】:

        猜你喜欢
        • 2020-02-04
        • 1970-01-01
        • 2011-06-26
        • 1970-01-01
        • 2022-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多