关闭时写入后无法访问套接字输入流的内容



我正在尝试读取刚刚关闭的套接字输入流的全部内容。然而,如果我只是试着正常读取它,我会不断地尝试在同一时间写入套接字的输出流。它看起来像是写入"中断"了套接字的输入流,使其无法读取剩余的内容。

举个小例子来说明我的问题:

@Test
public void socketTest() throws Throwable
{
new Thread(() -> {
try
{
ServerSocket serverSocket = new ServerSocket(5555);
while(true)
{
//keep listening
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
System.out.println(in.read());
Thread.sleep(2000);
try
{
socket.getOutputStream().write(3);
}
catch(Throwable ex)
{
System.out.println("Error writing");
ex.printStackTrace();
}
System.out.println(in.read());
in.close();
socket.close();
System.exit(0);
}
}
catch(Throwable ex)
{
System.out.println("Error reading");
ex.printStackTrace();
}
}).start();
Thread.sleep(100); //you might need this
Socket socket = new Socket("localhost", 5555);
OutputStream out = socket.getOutputStream();
out.write(1);
out.write(2);
out.close();
socket.close();
synchronized(this)
{
wait();
}
}

我得到输出:

1
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:170)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:223)
at net.jumpai.test.SocketTTest.lambda$socketTest$0(SocketTTest.java:55)
at java.lang.Thread.run(Thread.java:745)
Error reading

然而,2从未被读取,并且在流中写入3从未出错。我不在乎是否在流中写作,但我想读2。有办法做到这一点吗?

我使用的是带有jdk1.8.0_91的Windows7。即使在更新到jdk1.8.0_181(目前的最新版本(之后,我也可以重现这个问题。我还可以在另一台使用Java 1.8.0_111的Windows7计算机上进行复制。

无法在ArchLinux openjdk1.8.0_172上重现该错误,这是否意味着它是java版本的错误?它似乎真的与Windows有关,或者至少与Oracle的实施有关。

看到这种行为可以在任何版本的2台Windows机器上轻松复制,但在任何Linux或Mac系统上都无法复制,我怀疑这是Oracle JVM中的一个漏洞,open-jdk不存在这个漏洞。

这个bug很烦人,如果有人能发布一个作品,我将不胜感激。同时,我向Oracle提交了一份错误报告:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8208479

为了构建一个变通方案,我提供了这个功能,可以检查您的系统是否存在错误。

public static boolean hasOracleSocketBug()
{
try
{
int port = 10000;
while(!PortChecker.portAvailable(port))
port++;
ServerSocket serverSocket = new ServerSocket(port);
int fPort = port;
new Thread(() -> {
try
{
Socket socket = new Socket("localhost", fPort);
OutputStream out = socket.getOutputStream();
out.write(1);
out.write(2);
out.close();
socket.close();
}
catch(Exception ex)
{
try
{
serverSocket.close();
}
catch(IOException ignored) {}
}
}).start();
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
in.read();
socket.getOutputStream().write(3);
try
{
in.read();
}
catch(Exception ex)
{
socket.close();
return true;
}
in.close();
socket.close();
return false;
}
catch(Throwable ignored)
{
return false;
}
}
public static boolean portAvailable(int port)
{
ServerSocket ss = null;
try
{
ss = new ServerSocket(port);
ss.setReuseAddress(true);
return true;
}
catch(IOException ignored)
{
return false;
}
finally
{
if(ss != null)
{
try
{
ss.close();
}
catch(IOException ignored) {}
}
}
}

最新更新