为什么 DataoutputStream 和 BufferedWriter 的创建顺序很重要?
Why DataoutputStream and BufferedWriter creation order is important?
我正在尝试创建一个简单的客户端,首先我与服务器进行通信:
- 文件名
- 组成文件的块序列
所以对于第一个我想使用 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
中更改 BufferedWriter
和 DataOutputStream
之间的顺序,一切正常!
其实是因为想起了java课程的一些内容,所以想起来了,但是具体的记不太清了!你能帮我解决我的这个疑问吗?非常感谢! :)
因为关闭 BufferedWriter
会刷新它,如果您先创建编写器,它将最后关闭,在流之后,关闭它们中的任何一个都会关闭套接字。查看堆栈跟踪。 DataOutputStream
没有缓冲,所以刷新它什么都不做。
注意:
... 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.
None这个有道理。 InputStreamReader
没有 readLine()
方法,更不用说被弃用的方法了; OutputStreamWriter
写入字符,而不是字节。
首先,你的所作所为近乎疯狂。您似乎打算将文本和二进制数据写入同一流:
将很难控制两种数据的交错,因为您在堆栈中的那个点使用缓冲写入器。
即使你得到了交错的权利,"other end" 也有解开它来分离文本和二进制文件的问题。
您试图证明您的决定是正确的,如下所示在输出流上使用两个流堆栈:
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
,用writeUTF
写文件名和writeInt
+ write
写块。通过发送块大小为零来指示文件结尾。
(如果您事先知道要发送多少字节,也可以将文件作为一个大块发送。)
服务器端代码应该在 DataInputStream
.
上的调用中反映客户端
但是您看到的行为差异的原因是 try
初始化中声明的顺序决定了在 try
结束时关闭流的顺序块。
如果写入器先关闭则:
BufferedWriter.close()
-> BufferedWriter.flush() -> OutputStreamWriter.write()
-> OutputStreamWriter.close() -> SocketOutputStream.close()
DataOutputStream.close() -> SocketOutputStream.close()
这样就可以了,因为第二组closes不需要写入任何数据
如果writer被秒关闭则:
DataOutputStream.close() -> SocketOutputStream.close()
BufferedWriter.close()
-> BufferedWriter.flush() -> OutputStreamWriter.write() // FAIL
发生失败是因为刷新无法将数据写入套接字,因为您已经(隐式)关闭了套接字。
我正在尝试创建一个简单的客户端,首先我与服务器进行通信:
- 文件名
- 组成文件的块序列
所以对于第一个我想使用 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
中更改 BufferedWriter
和 DataOutputStream
之间的顺序,一切正常!
其实是因为想起了java课程的一些内容,所以想起来了,但是具体的记不太清了!你能帮我解决我的这个疑问吗?非常感谢! :)
因为关闭 BufferedWriter
会刷新它,如果您先创建编写器,它将最后关闭,在流之后,关闭它们中的任何一个都会关闭套接字。查看堆栈跟踪。 DataOutputStream
没有缓冲,所以刷新它什么都不做。
注意:
... 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.
None这个有道理。 InputStreamReader
没有 readLine()
方法,更不用说被弃用的方法了; OutputStreamWriter
写入字符,而不是字节。
首先,你的所作所为近乎疯狂。您似乎打算将文本和二进制数据写入同一流:
将很难控制两种数据的交错,因为您在堆栈中的那个点使用缓冲写入器。
即使你得到了交错的权利,"other end" 也有解开它来分离文本和二进制文件的问题。
您试图证明您的决定是正确的,如下所示在输出流上使用两个流堆栈:
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
,用writeUTF
写文件名和writeInt
+ write
写块。通过发送块大小为零来指示文件结尾。
(如果您事先知道要发送多少字节,也可以将文件作为一个大块发送。)
服务器端代码应该在 DataInputStream
.
但是您看到的行为差异的原因是 try
初始化中声明的顺序决定了在 try
结束时关闭流的顺序块。
如果写入器先关闭则:
BufferedWriter.close() -> BufferedWriter.flush() -> OutputStreamWriter.write() -> OutputStreamWriter.close() -> SocketOutputStream.close() DataOutputStream.close() -> SocketOutputStream.close()
这样就可以了,因为第二组closes不需要写入任何数据
如果writer被秒关闭则:
DataOutputStream.close() -> SocketOutputStream.close() BufferedWriter.close() -> BufferedWriter.flush() -> OutputStreamWriter.write() // FAIL
发生失败是因为刷新无法将数据写入套接字,因为您已经(隐式)关闭了套接字。