【问题标题】:Inconsistent result from DataInputStreamDataInputStream 的结果不一致
【发布时间】:2018-12-10 07:56:08
【问题描述】:

我在 Java 中使用 DataInputStream 和 DataOutputStream 时遇到问题,如果没有一些非常愚蠢的措施,我无法解决,我想知道是否有人知道更好的方法(我非常确定有一个,但我没有没找到)。

背景:我希望能够将 Json 字符串发送到服务器并让服务器将该字符串解析为对象。这需要独立于语言,因为我正在研究的项目需要我有一个异构系统。

为了最简单的例子来说明我的问题,我将排除 Gson/Json 等的创建,因为它可以用任何字符串重新创建。代码如下:

public class ServerSide {
    private final int PORT = 5001;
    private ServerSocket servSock;
    private Socket sock;
    private DataInputStream dis;
    private DataOutputStream dos;

    public ServerSide() throws IOException {
        servSock = new ServerSocket(PORT);
    }

    public void startListening() {
        try {
            sock = servSock.accept();
            dis = new DataInputStream(sock.getInputStream());
            byte[] bytes = new byte[1024];

            dis.read(bytes);

            String receivedMessage = new String(bytes);
            System.out.println("Message received: " + receivedMessage);
            sock.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

一个简单的测试类,它创建一个 ServerSide() 并在一段时间内调用 startListening(true)。

public class ClientSide {
    private final int PORT = 5001;
    private Socket socket;
    private DataOutputStream dos;

    public ClientSide() {

    }
    public void sendMessage(String m) {
        try {
            socket = new Socket("127.0.0.1", PORT);
            dos = new DataOutputStream(socket.getOutputStream());
            dos.writeBytes(m);
            System.out.println("Message sent: " + m);

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

一个简单的测试类,创建ClientSide()对象并调用sendMessage("this is a test message");

我遇到的问题是我只在服务器端收到部分消息。我不确定这是否是输入或输出流的问题,并且只能通过多次写入和读取数据并修剪它们之间的空白来找到解决方法。我的第一个问题是尝试将 Json 字符串发送到我用 c# 编写的程序时,它总是会发送第一个字符(“{”),然后在下一次读取时发送字符串的其余部分。我通过发送一个空格然后在服务器端忽略它来反驳这一点。当在第一次读取时读取看似随机数量的字符串时,java-java 上的问题会变得更糟。

上述代码在服务器端的一些示例输出是:

Message received: This is a tes

Message received: T

Message received: T

Message received: T

【问题讨论】:

    标签: java datainputstream


    【解决方案1】:

    is.read(bytes); may return any number of bytes - 从一到bytes.length

    从输入流中读取一些字节并将它们存储到缓冲区数组 b 中。实际读取的字节数以整数形式返回。在输入数据可用、检测到文件结尾或引发异常之前,此方法会一直阻塞。

    需要重复调​​用此方法,直到它返回 -1,这意味着流结束。

    Reading from and Writing to a Socket 提供了如何在面向行的交换中读取所有数据的示例。对于二进制数据(DataStream 实际生成的数据),您需要结合使用 ByteArrayOutputStream、ByteArrayInputStream 和 DataInputStream:

    InputStream sis = sock.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    while (int len = sis.read(bytes) > 0) {
        baos.write(bytes, 0, len);
    }
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    DataInputStream dis = new DataInputStream(bais);
    byte[] vBytes = new byte[baos.size()];
    int sLen = dis.read(vBytes);
    String receivedMessage = new String(vBytes, 0, sLen);
    System.out.println("Message received: " + receivedMessage);
    

    注意:上面的代码正在回答特定问题。不要将其投入生产:)

    【讨论】:

    • 太棒了。非常感谢。作为对此的后续行动,您将如何解决此问题?
    • @KevinAdams 首先使用writeInt 写入长度,在读取时使用readIntreadFully 读取与预期长度完全相同的字节数。
    【解决方案2】:

    感谢@Illya Kysil 提供的帮助,我通过将代码更改为:

    public class ClientSide {
        private final int PORT = 5001;
        private Socket socket;
        private DataOutputStream dos;
    
        public ClientSide() {
    
        }
        public void sendMessage(String m) {
            try {
                socket = new Socket("127.0.0.1", PORT);
                dos = new DataOutputStream(socket.getOutputStream());
                byte[] mBytes = m.getBytes();
                dos.writeInt(mBytes.length);
                dos.write(mBytes);
                System.out.println("Message sent: " + m);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

    public class ServerSide {
        private final int PORT = 5001;
        private ServerSocket servSock;
        private Socket sock;
        private DataInputStream dis;
        private DataOutputStream dos;
    
        public ServerSide() throws IOException {
            servSock = new ServerSocket(PORT);
        }
    
        public void startListening() {
            try {
                sock = servSock.accept();
                dis = new DataInputStream(sock.getInputStream());
                int length = dis.readInt();
                byte[] bytes = new byte[length];
                dis.readFully(bytes);
                String receivedMessage = new String(bytes);
                System.out.println("Message received: " + receivedMessage.trim());
                dis.close();
                sock.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
    }
    

    这将首先发送一个等于正在发送消息的字节数组大小的整数。可以在服务器端创建这个长度的缓冲区,可以使用 DataInputStream 的 readFully 方法来填充这个缓冲区。

    【讨论】:

      猜你喜欢
      • 2014-09-21
      • 2021-03-27
      • 2021-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多