为什么即使套接字关闭,socket.isOutputShutdown()也会返回false?

时间:2015-02-12 00:36:08

标签: java sockets

Java Socket API告诉我关闭套接字也会关闭套接字InputStreamOutputStream

Socket API Javadoc和Input / OutputStream API信息都没有定义(我还没有发现它)" shutdown "意味着OutputStreamInputStream,但我一直假设关闭要么将它们放入" 关闭"状态。

但是,在我成功调用我的客户端套接字close()方法之后(调用isClosed()返回true),如果我然后调用该套接字isInputShutdown() {{ 1}}或isOutputShutdown()方法,结果为false

我确信在调用套接字close()时,两个流都没有未读/未发送的数据。

我假设我不明白"是关闭"用于套接字输入/输出流的装置,和/或关闭时的装置。

3 个答案:

答案 0 :(得分:3)

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

socket.isOutputShutdown() ONLY 的调用返回true,此前已调用 socket.shutdownOutput()。 你说这个文件在这一点上有点误导是正确的。

this thread中,将更详细地解释 socket.shutdownOutput()的使用。基本上你可以使用它创建一个半封闭的连接(意味着你的结束决定不再发送数据,但愿意从另一端收听更多数据)。

答案 1 :(得分:2)

<强> TL; DR

无论您做什么,在致电true后获取isOutputShutdown的唯一方法是之前致电shutdownOutput。无论Socket的状态如何。输出确实需要直接关闭以使方法返回true,而不仅仅是通过继承关闭。


解决此问题的最佳方法是查看源代码;)

首先在java.net.Socket中搜索方法isOutputShutdown:

public boolean isOutputShutdown() {
    return shutOut;
}

它只是一个访问者,好吧,然后搜索shutOut。然后我们会注意到只有在调用方法shutdownOutput时才将值设置为true(这是合乎逻辑的)

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;
}

请注意,如果套接字已关闭,我们会抛出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 Sockets 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;

再一次,多么可惜,我为此而努力......

所以,让我们进入PlainSocketImpl的课程AbstractPlainSocketImpl

native void socketShutdown(int howto) throws IOException;

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

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);
}

现在我认为您已经非常了解该方法如何真正关闭流。

  

什么&#34;关闭&#34;表示OutputStream或InputStream

但为什么isOutputShutdown返回false而isClosed返回true?

考虑到shutOut布尔值仅在方法结束时设置为true 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。但即使该方法返回false,它实际上也是关闭的,因为如果Socket所基于的Stream已经关闭,它就无法打开。

答案 2 :(得分:1)

shutdownOutput()导致在任何待处理数据之后发送FIN,对等体将视为流的末尾。

如果您已调用 shutdownOutput(),则isOutputShutdown()将返回true.在所有其他情况下,无论套接字或连接的其他状态如何,它返回false。

同样适用于:

  • shutdownInput()isInputShutdown()
  • close()isClosed()
  • connect()或使用参数构建new Socket(...)isConnected()

除非您的应用程序调用关联的API,否则这些API都不会神奇地更改状态。唯一的例外是isBound(),如果您拨打bind()connect(),或者使用参数构建new Socket(...),或者通过{{1 }}

根据 peer 连接的影响,它们都不会改变状态。具体来说,{ {1}}并不意味着对等方关闭了连接。这意味着关闭这个套接字。