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

标签 java sockets client bufferedwriter dataoutputstream

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

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

因此,对于第一个,我想使用 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)

奇怪的是,如果我改变BufferedWriter之间的顺序和 DataOutputStream里面try一切正常!

其实这个想法是因为我记得java类(class)中的一些内容,但我不太记得细节了!您能帮我解答一下我的这个疑问吗?多谢! :)

最佳答案

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

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

  • 即使您正确地进行了交错,“另一端”也存在将其拆开以分离文本和二进制的问题。

<小时/>

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

So for the first one I thought to use to a BufferedWriter: this choice was made since I can't use on the server a InputStreamReader from the moment that the readLine() method is deprecated. However, for the second one I used a OutputStreamWriter since it is the better (only?) one choice to write a byte array on a socket.

我不明白你的逻辑。但一种方法行不通的事实并不一定意味着(任何)其他方法行得通。

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

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

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

<小时/>

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

  • 如果先关闭 writer,则:

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

    这是可以的,因为第二组close不需要写入任何数据。

  • 如果 writer 第二次关闭,则:

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

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

关于java - 为什么 DataoutputStream 和 BufferedWriter 创建顺序很重要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27882426/

相关文章:

c++ - 在 MFC 中使用套接字

java - 云存储与数据存储延迟

java - 给出 Java 项目 (Netbeans) 中文件的固定路径

java - 从java中的本地主机连接的套接字获取IP

java - 当数据包未从服务器发送时,如何在 UDP 客户端中设置超时?

c - 如何使用 pthreads 在 C 中的 udpclient 中发送和接收

java - 来自 Android 客户端的 HTTP 请求

java - 我有一个 keystore 文件,如何在 Android 应用程序中为 sslContext 提供 keyManagers?

java - 将 byte[] 写入 OutputStream 时添加缓冲区

java - 在运行时访问strings.xml进行解析