为什么DataoutputStream和BufferedWriter创建顺序很重要?

时间:2015-01-10 23:49:16

标签: java sockets client bufferedwriter dataoutputstream

我正在尝试创建一个简单的客户端,首先我将其发送到服务器:

  1. 文件名
  2. 组成文件的块序列
  3. 所以对于我想用于BufferedWriter的第一个:这个选择是因为我不能在服务器上使用一个InputStreamReader,因为不推荐使用readLine()方法。但是,对于第二个,我使用了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)
    

    奇怪的是,如果我更改BufferedWriterDataOutputStream内部try之间的顺序,那么就可以了!

    实际上这个想法已经来了,因为我记得java课程中的一些内容,但我真的不记得细节了!你能帮助我解决这个问题吗?非常感谢! :)

2 个答案:

答案 0 :(得分:1)

首先,你所做的是边缘疯狂。您似乎打算在同一个流中写入文本和二进制数据:

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

  • 即使您获得了交错权,"另一端"有解开文本和二进制文件的问题。


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

  

因此对于我想用于BufferedWriter的第一个:这个选择是因为我不能在不推荐使用readLine()方法的情况下在服务器上使用InputStreamReader。但是,对于第二个,我使用了OutputStreamWriter,因为在套接字上写一个字节数组是更好的(只有?)选择。

我不遵循你的逻辑。但是,一种方法不起作用的事实并不一定意味着(任何)其他方法将会发挥作用。

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

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

服务器端代码应该在DataInputStream的调用中镜像客户端。


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

  • 如果作者先被关闭,那么:

    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
    

    发生故障是因为flush已经(隐式)关闭了它而无法将数据写入套接字。

答案 1 :(得分:0)

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

NB:

  

...因为我不能在不推荐使用readLine()方法的情况下在服务器上使用InputStreamReader。但是,对于第二个,我使用了OutputStreamWriter,因为在套接字上写一个字节数组是更好的(只有?)选择。

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