以前使用很少使用java的ServerSocket编程,主要使用nio的ServerSocketChannel.从没有注意到这个问题,客户端的socket发出Close指令后,服务器为什末是抛出异常关闭.而不是更柔和的办法.看了看jdk的c代码,Java的Socket读取函数 switch (WSAGetLastError()) { case WSAEINTR: JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed"); break; case WSAECONNRESET: case WSAESHUTDOWN: /* * Connection has been reset - Windows sometimes reports * the reset as a shutdown error. */ JNU_ThrowByName(env, "sun/net/ConnectionResetException", ""); break;
case WSAETIMEDOUT : JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException", "Read timed out"); break;
default: NET_ThrowCurrent(env, "recv failed"); }如果调用WSAGetLastError(),客户端关闭连接,返回多数是WSAECONNRESET,这是jvm要抛出ConnectionResetException,注意这不是java的标准异常,我们没有捕捉它.这里是微软msdn 错误码的详细描述 http://msdn2.microsoft.com/en-us/library/ms740668.aspx打开SocketInputStream.java // acquire file descriptor and do the read FileDescriptor fd = impl.acquireFD(); try { n = socketRead0(fd, b, off, length, impl.getTimeout()); if (n > 0) { return n; } } catch (ConnectionResetException rstExc) { gotReset = true; } finally { impl.releaseFD(); }
/* * We receive a "connection reset" but there may be bytes still * buffered on the socket */ if (gotReset) { impl.setConnectionResetPending(); impl.acquireFD(); try { n = socketRead0(fd, b, off, length, impl.getTimeout()); if (n > 0) { return n; } } catch (ConnectionResetException rstExc) { } finally { impl.releaseFD(); } }
/* * If we get here we are at EOF, the socket has been closed, * or the connection has been reset. */ if (impl.isClosedOrPending()) { throw new SocketException("Socket closed"); } if (impl.isConnectionResetPending()) { impl.setConnectionReset(); } if (impl.isConnectionReset()) { throw new SocketException("Connection reset"); } eof = true; return -1; }注意抛出ConnectionResetException后,socket的buffer还有数据,最后如果connection被重置,抛出SocketException,这个异常是IOException的子类,大家catch IOException就行了.所以客户端的socket发出Close指令后,服务器是抛出异常关闭.对此sun也有它的苦衷,java的io设计是跨平台,代码移植不应该有任何差异,所以只好提取了所有平台的共性.java的nio针对各个平台写的,写出的代码不保证跨平台的移植.另外说一句题外话,我看Java Network Programming, 3rd Edition时,发现客户端关闭连接,抛出InterruptedIOException,这个异常一般是超时抛出的,原因就是I/O 操作已中断信号。抛出 InterruptedIOException 指示输入或输出传输已经终止,原因是执行此操作的线程中断。我得继续研究一下,windows网络编程了,多年不用,生锈了,这次得到好友kei和cyt的帮助,表示感谢.
经好友象风确认,对于阻塞模式,只要客户端不关闭,或者网络不断开,服务器端的socket连接不关闭,阅读Java Network Programming, 3rd Editionpublic boolean isConnected( ) // Java 1.4
The name is a little misleading. It does not tell you if the socket is currently connected to a remote host (that is, if it is unclosed). Instead it tells you whether the socket has ever been connected to a remote host. If the socket was able to connect to the remote host at all, then this method returns true, even after that socket has been closed. To tell if a socket is currently open, you need to check that isConnected( ) returns true and isClosed() returns false. For example:boolean connected = socket.isConnected( ) && ! socket.isClosed( );问了问,Thomas Schodt,告诉我This will tell you ifclose()connect()etc.have been called on the socket at *your* end, not at the *other* end |