【问题标题】:Why DataoutputStream and BufferedWriter creation order is important? [duplicate]为什么 DataoutputStream 和 BufferedWriter 创建顺序很重要? [复制]
【发布时间】:2015-01-10 23:49:16
【问题描述】:

我正在尝试创建一个简单的客户端,首先我与服务器通信:

  1. 文件名
  2. 组成文件的块序列

所以对于第一个我想使用 BufferedWriter 的方法:做出这个选择是因为从不推荐使用 readLine() 方法的那一刻起,我就不能在服务器上使用 InputStreamReader。但是,对于第二个,我使用了 OutputStreamWriter,因为在套接字上写入字节数组是更好的(唯一?)一种选择。

所以,这是我的客户端代码的第一个版本:

public class Client
{
    private static final int PART_SIZE = 1000000; // 1MB

    public static void main(String[] args) throws IOException
    {
        final Path file = Paths.get(args[0]);
        final String filenameBase = file.getFileName().toString();
        final byte[] buf = new byte[PART_SIZE];    
        Socket socket = new Socket(InetAddress.getLocalHost(),8080);
        System.out.println("Socket created");
        int partNumber = 0;
        Path part;
        int bytesRead;
        byte[] toWrite;

        try (
            final InputStream in = Files.newInputStream(file);
            final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            final DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
        ) {
            System.out.println("closed="+socket.isClosed());
            bw.write(filenameBase,0,filenameBase.length());
            //other stuff for the chunk creation and spedition
        }
    }
}

但是,如果我运行此代码,则会发生此异常:

Exception in thread "main" java.net.SocketException: Socket closed
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:121)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
    at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
    at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:316)
    at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:149)
    at java.io.OutputStreamWriter.close(OutputStreamWriter.java:233)
    at java.io.BufferedWriter.close(BufferedWriter.java:266)
    at PAD.Charlie.Client.App.main(App.java:50)

奇怪的是,如果我在try 中更改BufferedWriterDataOutputStream 之间的顺序,一切正常!

其实这个想法是因为我从java课程中想起了一些关于它的东西,但是我真的不记得细节了!你能帮我解决我的这个疑问吗?非常感谢! :)

【问题讨论】:

    标签: java sockets client bufferedwriter dataoutputstream


    【解决方案1】:

    首先,你正在做的事情是疯狂的。您似乎打算将文本和二进制数据写入同一个流:

    • 由于您在堆栈中的那个点使用缓冲写入器,因此很难控制两种数据的交错。

    • 即使你得到正确的交错,“另一端”也有解开它以分离文本和二进制的问题。


    您尝试在输出流上使用两个流堆栈来证明您的决定,如下所示:

    所以对于第一个我想使用 BufferedWriter 的方法:做出这个选择是因为从不推荐使用 readLine() 方法的那一刻起,我就不能在服务器上使用 InputStreamReader。但是,对于第二个,我使用了 OutputStreamWriter,因为在套接字上写入字节数组是更好的(唯一?)一种选择。

    我不遵循你的逻辑。但一种方法不起作用这一事实并不一定意味着(任何)另一种方法会起作用。

    如果您想要一个可行的解决方案,那么我可以想到一些。最简单的就是在客户端用DataOutputStreamonly,用writeUTF写文件名,writeInt+write写chunk。通过发送一个大小为零的块来指示文件的结尾。

    (如果您事先知道要发送多少字节,您也可以将文件作为一大块发送。)

    服务器端代码应在其对DataInputStream 的调用中反映客户端。


    但您看到的行为差异的原因是 try 初始化中的声明顺序决定了在 try 块末尾关闭流的顺序。

    • 如果 writer 先关闭,则:

      BufferedWriter.close() 
          -> BufferedWriter.flush() -> OutputStreamWriter.write()
          -> OutputStreamWriter.close() -> SocketOutputStream.close()
      DataOutputStream.close() -> SocketOutputStream.close()
      

      这样就可以了,因为第二组关闭不需要写入任何数据。

    • 如果作者第二次关闭,则:

      DataOutputStream.close() -> SocketOutputStream.close()
      BufferedWriter.close() 
          -> BufferedWriter.flush() -> OutputStreamWriter.write()  // FAIL
      

      失败是因为刷新无法将数据写入套接字,因为您已经(隐式)关闭了它。

    【讨论】:

    • 那么您能否建议我一种编写方法:1. 一个字符串仅一次 2. 一个字节数组大小的数字 3. 一个字节数组 4. 重复 2. 直到整个文件被发送
    • 文件名使用writeUTF()readUTF(),完全摆脱读者和作者。对于大小,writeInt()writeLong() 等。
    【解决方案2】:

    因为关闭BufferedWriter 会刷新它,如果您先创建写入器,它将在流之后最后关闭,关闭它们中的任何一个都会关闭套接字。查看堆栈跟踪。 DataOutputStream 没有缓冲,所以刷新它什么都不做。

    注意:

    ...因为从不推荐使用 readLine() 方法的那一刻起,我就无法在服务器上使用 InputStreamReader 。但是,对于第二个,我使用了 OutputStreamWriter,因为在套接字上写入字节数组是更好的(唯一?)一种选择。

    这些都没有意义。 InputStreamReader 没有 readLine() 方法,更不用说已弃用的方法了; OutputStreamWriter 写入的是字符,而不是字节。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-07
      • 1970-01-01
      • 2013-05-18
      • 1970-01-01
      • 2013-11-18
      • 1970-01-01
      相关资源
      最近更新 更多