Java Socket API告诉我关闭套接字也会关闭套接字InputStream
和OutputStream
。
Socket API Javadoc和Input / OutputStream API信息都没有定义(我还没有发现它)" shutdown "意味着OutputStream
或InputStream
,但我一直假设关闭要么将它们放入" 关闭"状态。
但是,在我成功调用我的客户端套接字close()
方法之后(调用isClosed()
返回true
),如果我然后调用该套接字isInputShutdown()
{{ 1}}或isOutputShutdown()
方法,结果为false
。
我确信在调用套接字close()
时,两个流都没有未读/未发送的数据。
我假设我不明白"是关闭"用于套接字输入/输出流的装置,和/或关闭时的装置。
答案 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}}并不意味着对等方关闭了连接。这意味着你关闭这个套接字。