2015-02-12 114 views
4

Java Socket API告诉我关闭套接字也会关闭套接字的InputStreamOutputStream即使套接字关闭,为什么socket.isOutputShutdown()返回false?

既不的Socket API的Javadoc,也不是输入/ OutputStream的API信息定义了(我还没有找到它)正是“关机”是指为OutputStreamInputStream,但我一直在假设关闭或者看跌期权他们变成了“关机”状态。

然而,当我成功地调用我的客户套接字的close()方法(调用isClosed()回报true),如果我然后调用要么插座的isInputShutdown()isOutputShutdown()方法,结果是false

我相信,在调用Socket的close()时,这两个流都没有未读/未发送的数据。

我假设我不明白什么“关机”意味着套接字的输入/输出流,和/或关闭发生时。

+1

我相信你的意思是isOutputShutdown? – 2015-02-12 00:42:59

+0

谢谢@ Jean-FrançoisSavard - 我编辑了这个问题,将“isInputStreamShutdown”和“isOutputStreamShutdown”更改为这两个地方的正确方法名称。 – DigSigBlake 2015-02-12 01:00:25

回答

3

即使调用socket.getOutputStream()。close()方法,在socket.isOutputShutdown后()调用将返回false。 此外,如果套接字尚未连接,socket.isOutputShutdown()将返回false。

socket.isOutputShutdown()ONLY返回true,当socket.shutdownOutput()以前调用的invokation。 你说得对,该文件在这一点上有点误导。

this thread中,使用socket.shutdownOutput()进行了更详细的说明。基本上你可以用它来创建一个半关闭的连接(意味着你的终端决定不再发送数据,但愿意从另一端收听更多的数据)。

1

shutdownOutput()导致FIN在任何待处理的数据之后被发送,该数据将被视为流的结束。

如果有一个名为shutdownOutput()isOutputShutdown()然后将返回true.在所有其他情况下,且不论插座或连接的其他状态,则返回false。

类似地,对于:

  • shutdownInput()isInputShutdown()
  • close()isClosed()
  • connect()或构建new Socket(...)带参数,和isConnected()

除非您的应用程序调用关联的API,否则这些API都不会奇迹般地改变状态。唯一的例外是isBound(),它可以成为真正的,如果你打电话bind(),或connect(),或构建带有参数的new Socket(...),或通过ServerSocket.accept().

他们没有得到一个改变取决于状态是什么对等确实与连接。具体来说,isClosed()并不意味着对等关闭了连接。这意味着你的关闭了这个插座。

2

TL; DR

不管你做什么,要求isOutputShutdown后得到true的唯一方法就是之前调用shutdownOutput。不管Socket的状态如何。输出真的需要直接关闭,方法返回true,而不仅仅是通过继承关闭。


解决这个是看源代码的最佳方式;)

让在java.net.Socket搜索方法isOutputShutdown开始:

public boolean isOutputShutdown() { 
    return shutOut; 
} 

它只是一个访问,那么好吧让搜索shutOut。然后,我们会发现,调用方法shutdownOutput(这是合乎逻辑)

public void shutdownOutput() throws IOException 
{ 
    if (isClosed()) 
     throw new SocketException("Socket is closed"); 
    if (!isConnected()) 
     throw new SocketException("Socket is not connected"); 
    if (isOutputShutdown()) 
     throw new SocketException("Socket output is already shutdown"); 
    getImpl().shutdownOutput(); 
    shutOut = true; 
} 

注意这里只当值设置为true,我们抛出一个SocketException如果关闭套接字它到现在为止,确认你以为关闭Socket也关闭它。现在让我们看看getImpl来找到它参考的内容,并从它的引用中检查方法shutdownOutput

SocketImpl getImpl() throws SocketException { 
    if (!created) 
     createImpl(true); 
    return impl; 
} 

该方法的javadoc指定它返回连接到此套接字的SocketImpl。如果需要的话创建它,但我怀疑它在我们的调试中相当可观。 SocketImpl只是一个抽象类,我们必须找到哪个实现会覆盖shutdownOutput方法。

现在我们能够找到的实现是利用工厂得到一个instance

factory.createSocketImpl() 

因此,让我们来看看SocketImplFactory。那么这个类是一个接口,只有一行SocketImpl createSocketImpl();。那么如何给出SicketImpl的实例,其中真正定义了方法shutdownOutput

让我们来看看AbstractPlainSocketImpl(默认套接字创建),它扩展了我们的SocketImpl,它在Steven B. Byrne先生的顶层javadoc中声明,这是默认的套接字实现。所以,我认为从这里我们应该能够很好地了解shutdownOutput究竟在做什么。

/** 
* Shutdown read-half of the socket connection; 
*/ 
protected void shutdownInput() throws IOException { 
    if (fd != null) { 
     socketShutdown(SHUT_RD); 
     if (socketInputStream != null) { 
      socketInputStream.setEOF(true); 
     } 
     shut_rd = true; 
    } 
} 

/** 
* Shutdown write-half of the socket connection; 
*/ 
protected void shutdownOutput() throws IOException { 
    if (fd != null) { 
     socketShutdown(SHUT_WR); 
     shut_wr = true; 
    } 
} 

有趣,不是吗?为了我们的调试,请考虑fd在我们的情况下不为空,并直接转到socketShutdown

但是等一下!什么是SHUT_WR?

好的问题,正如@EJP在评论中指出的,它们是Berkeley套接字API的一个长期部分。他们只需指定如何进行(0读,1写入)

public final static int SHUT_RD = 0; 
public final static int SHUT_WR = 1; 

现在回到socketShutdown

abstract void socketShutdown(int howto) 
    throws IOException; 

再次,真可惜,我因子评分我们有它...

因此,让我们继续AbstractPlainSocketImpl的课PlainSocketImpl

native void socketShutdown(int howto) throws IOException; 

我们可以找到java.net.PlainSocketImpl.chere的源代码。现在让我们看看我们的方法

Java_java_net_PlainSocketImpl_socketShutdown(JNIEnv *env, jobject this, 
              jint howto) 
{ 

    jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID); 
    jint fd; 

    /* 
    * WARNING: THIS NEEDS LOCKING. ALSO: SHOULD WE CHECK for fd being 
    * -1 already? 
    */ 
    if (IS_NULL(fdObj)) { 
     JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", 
         "socket already closed"); 
     return; 
    } else { 
     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID); 
    } 
    JVM_SocketShutdown(fd, howto); 
} 

的代码现在我认为你有怎样的方法真的着手关闭流心中有数。

什么是 “关机” 是指一个OutputStream或InputStream的

但是为什么isOutputShutdown返回假,而isClosed回是真的吗?

那么考虑shutOut布尔仅在方法shutdownOutput

public void shutdownOutput() throws IOException 
{ 
    if (isClosed()) 
     throw new SocketException("Socket is closed"); 
    if (!isConnected()) 
     throw new SocketException("Socket is not connected"); 
    if (isOutputShutdown()) 
     throw new SocketException("Socket output is already shutdown"); 
    getImpl().shutdownOutput(); 
    shutOut = true; 
} 

这意味着,在默认情况下关闭Socket不说布尔值设置为true结束设置为true。但即使该方法返回false,它实际上也会关闭,因为如果SocketStream基于关闭,它将无法打开。

+0

'SHUT_RD'和'SHUT_WR'是伯克利套接字API的一个长期部分。 – EJP 2015-02-12 01:21:18

+0

@EJP感谢精确度,编辑。 – 2015-02-12 01:24:49

+0

@ Jean-FrançoisSavard(以及其他所有人)非常感谢你剖析产生行为的代码。我不会说Socket通信状态机的这部分实现的很好,但现在我明白了。 – DigSigBlake 2015-02-12 17:30:45